SpringBoot啟動流程之引導上下文DefaultBootstrapContext的過程
前言
前文深入解析SpringApplication構造方法,而接下來的幾篇文章將重點介紹run方法的執(zhí)行邏輯。

SpringBoot版本2.7.18的SpringApplication的run方法的執(zhí)行邏輯如下,本文將詳細介紹第一小節(jié):創(chuàng)建引導上下文
// SpringApplication類方法
public ConfigurableApplicationContext run(String... args) {
// 記錄應用啟動的開始時間
long startTime = System.nanoTime();
// 1.創(chuàng)建引導上下文,用于管理應用啟動時的依賴和資源
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置無頭模式屬性,以支持在無圖形環(huán)境下運行
// 將系統(tǒng)屬性 java.awt.headless 設置為 true
configureHeadlessProperty();
// 2.獲取Spring應用啟動監(jiān)聽器,用于在應用啟動的各個階段執(zhí)行自定義邏輯
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3.發(fā)布開始事件、通知ApplicationListener監(jiān)聽器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4.解析應用參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 5.準備應用環(huán)境,包括讀取配置文件和設置環(huán)境變量
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置是否忽略 BeanInfo,以加快啟動速度
configureIgnoreBeanInfo(environment);
// 6.打印啟動Banner
Banner printedBanner = printBanner(environment);
// 7.創(chuàng)建應用程序上下文
context = createApplicationContext();
// 設置應用啟動的上下文,用于監(jiān)控和管理啟動過程
context.setApplicationStartup(this.applicationStartup);
// 8.準備應用上下文,包括加載配置、添加 Bean 等
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 9.刷新上下文,完成 Bean 的加載和依賴注入
refreshContext(context);
// 10.刷新后的一些操作,如事件發(fā)布等
afterRefresh(context, applicationArguments);
// 計算啟動應用程序的時間,并記錄日志
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 11.通知監(jiān)聽器應用啟動完成
listeners.started(context, timeTakenToStartup);
// 12.調(diào)用應用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便執(zhí)行自定義的啟動邏輯
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 13.處理啟動過程中發(fā)生的異常,并通知監(jiān)聽器
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 14.計算應用啟動完成至準備就緒的時間,并通知監(jiān)聽器
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
// 處理準備就緒過程中發(fā)生的異常
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回已啟動并準備就緒的應用上下文
return context;
}一、入口
// 1.創(chuàng)建引導上下文,用于管理應用啟動時的依賴和資源DefaultBootstrapContext bootstrapContext = createBootstrapContext();
bootstrapRegistryInitializers就是上一篇文章中在SpringApplication構造方法中創(chuàng)建的引導注冊組件初始化器集合(查詢spring.factories文件,沒有找到BootstrapRegistryInitializer的實現(xiàn)類)- 調(diào)用初始化器的initialize方法,參數(shù)為bootstrapContext,也就是說每個初始化器都會對bootstrapContext進行必要的設置和準備(
啟動時需要的資源和依賴) - 本方法是在run方法最開始調(diào)用的,也就是說引導注冊組件初始化器組件的
執(zhí)行時機最早了
主要內(nèi)容就是實例化DefaultBootstrapContext以及遍歷BootstrapRegistryInitializer集合調(diào)用initialize,下面詳細介紹下這兩個類的作用。
// SpringApplication類屬性方法
// 引導注冊初始化器
private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
private DefaultBootstrapContext createBootstrapContext() {
// 創(chuàng)建一個 DefaultBootstrapContext 實例,用于管理應用啟動時的資源和依賴
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 遍歷 bootstrapRegistryInitializers 集合中的每個 initializer,
// 并調(diào)用它們的 initialize 方法,將 bootstrapContext 作為參數(shù)傳入。
// 這一步確保每個 initializer 都可以對 bootstrapContext 進行相應的配置,
// 為應用程序的啟動過程準備所需的資源。
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
// 返回已完成初始化的 DefaultBootstrapContext 對象
return bootstrapContext;
}二、DefaultBootstrapContext
DefaultBootstrapContext作為SpringBoot啟動過程中的核心組件,負責環(huán)境配置、資源管理和生命周期管理,確保應用程序的順利啟動和運行。理解其作用有助于開發(fā)者更好地掌握SpringBoot的內(nèi)部機制。
類圖如下:

1、BootstrapRegistry接口
一個簡單的對象注冊表,在啟動和處理環(huán)境配置期間可用,直到ApplicationContext準備好為止。提供對單例的惰性訪問,這些單例的創(chuàng)建成本可能很高,或者需要在ApplicationContext可用之前共享。
注冊表使用Class作為鍵,這意味著只能存儲給定類型的單個實例。
addCloseListener(ApplicationListener)方法可用于添加監(jiān)聽器,當BootstrapContext關閉且ApplicationContext已準備好時,該監(jiān)聽器可以執(zhí)行某些操作。例如,實例可以選擇將自身注冊為常規(guī)SpringBean,以便可供應用程序使用。
public interface BootstrapRegistry {
// 注冊特定類型到注冊表。如果指定的類型已注冊且未以單例形式獲取,則將替換。
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
// 如果尚未存在,則注冊特定類型到注冊表。
<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);
// 返回給定類型是否已經(jīng)注冊。
<T> boolean isRegistered(Class<T> type);
// 返回給定類型的已注冊 {@link InstanceSupplier},如果沒有則返回 null。
<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);
// 添加 {@link ApplicationListener},當 {@link BootstrapContext} 關閉且
// {@link ApplicationContext} 準備就緒時,將調(diào)用該監(jiān)聽器,并傳遞 {@link BootstrapContextClosedEvent}。
void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);
// 提供所需時創(chuàng)建實際實例的供應者。
@FunctionalInterface
interface InstanceSupplier<T> {
// 工廠方法,在需要時創(chuàng)建實例。
T get(BootstrapContext context);
// 返回所提供實例的作用域。
default Scope getScope() {
return Scope.SINGLETON;
}
// 返回一個具有更新 {@link Scope} 的新 {@link InstanceSupplier}。
default InstanceSupplier<T> withScope(Scope scope) {
Assert.notNull(scope, "Scope must not be null");
InstanceSupplier<T> parent = this;
return new InstanceSupplier<T>() {
@Override
public T get(BootstrapContext context) {
return parent.get(context);
}
@Override
public Scope getScope() {
return scope;
}
};
}
// 工廠方法,用于為給定實例創(chuàng)建 {@link InstanceSupplier}。
static <T> InstanceSupplier<T> of(T instance) {
return (registry) -> instance;
}
// 工廠方法,用于從 {@link Supplier} 創(chuàng)建 {@link InstanceSupplier}。
static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
return (registry) -> (supplier != null) ? supplier.get() : null;
}
}
// 實例的作用域。
enum Scope {
// 單例實例。 {@link InstanceSupplier} 將僅被調(diào)用一次,并且每次都將返回相同的實例。
SINGLETON,
// 原型實例。 {@link InstanceSupplier} 將在每次需要實例時調(diào)用。
PROTOTYPE
}
}總結:用于注冊引導階段的組件,在應用啟動時通過register方法動態(tài)添加對象
2、BootstrapContext接口
一個簡單的引導上下文,在啟動和處理環(huán)境配置期間可用,直到ApplicationContext準備好為止。提供對單例的惰性訪問,這些單例的創(chuàng)建成本可能很高,或者需要在ApplicationContext可用之前共享。
public interface BootstrapContext {
// 如果類型已注冊,則從上下文中返回實例。如果之前未訪問過該實例,則會創(chuàng)建該實例
<T> T get(Class<T> type) throws IllegalStateException;
// 如果類型已注冊,則返回上下文中的實例。如果尚未訪問該實例,則將創(chuàng)建它。
// 如果類型未注冊,則返回指定的替代實例。
<T> T getOrElse(Class<T> type, T other);
// 如果類型已注冊,則返回上下文中的實例。如果尚未訪問該實例,則將創(chuàng)建它。
// 如果類型未注冊,則使用指定的供應者提供的實例。
<T> T getOrElseSupply(Class<T> type, Supplier<T> other);
// 如果類型已注冊,則返回上下文中的實例。如果尚未訪問該實例,則將創(chuàng)建它。
// 如果類型未注冊,則拋出由供應者提供的異常。
<T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X;
// 返回給定類型是否存在注冊
<T> boolean isRegistered(Class<T> type);
}總結:用于提供對引導階段注冊組件的只讀訪問,一旦BootstrapRegistry注冊完成并構建成BootstrapContext,所有組件可以通過get方法被安全地訪問,直到應用啟動完成。
3、DefaultBootstrapContext實現(xiàn)類
ConfigurableBootstrapContext是一個空接口,所以直接看核心內(nèi)容DefaultBootstrapContext
public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext {
} DefaultBootstrapContext是一個實現(xiàn)了ConfigurableBootstrapContext接口的類,主要用于管理應用啟動過程中的實例供應者和實例,提供了注冊、獲取、關閉監(jiān)聽等功能。以下是主要功能的總結:
1.實例供應者管理:
- 使用
Map<Class<?>, InstanceSupplier<?>> instanceSuppliers來存儲類型到實例供應者的映射,可以通過register和registerIfAbsent方法來注冊實例供應者 register方法支持覆蓋現(xiàn)有的實例供應者,而registerIfAbsent則僅在類型未注冊的情況下注冊實例供應者
2.實例管理:
- 使用
Map<Class<?>, Object> instances存儲已創(chuàng)建的實例 - 提供
get、getOrElse、getOrElseSupply等方法來獲取實例。如果實例尚未創(chuàng)建,則調(diào)用相應的實例供應者來創(chuàng)建實例,并在單例作用域下將其存儲
3.事件管理(后面文章調(diào)用時候細講):
- 使用
ApplicationEventMulticaster來管理事件的發(fā)布和監(jiān)聽 - 提供
addCloseListener方法添加關閉監(jiān)聽器,并在close方法中發(fā)布BootstrapContextClosedEvent事件
public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
// 存儲實例供應者的映射
private final Map<Class<?>, InstanceSupplier<?>> instanceSuppliers = new HashMap<>();
// 存儲已創(chuàng)建實例的映射
private final Map<Class<?>, Object> instances = new HashMap<>();
// 事件廣播器,用于發(fā)布應用事件
private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();
@Override
public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) {
// 注冊特定類型的實例供應者
register(type, instanceSupplier, true);
}
@Override
public <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier) {
// 如果尚未注冊,則注冊特定類型的實例供應者
register(type, instanceSupplier, false);
}
private <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier, boolean replaceExisting) {
// 檢查類型和實例供應者是否為空
Assert.notNull(type, "Type must not be null");
Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
synchronized (this.instanceSuppliers) {
// 檢查類型是否已注冊
boolean alreadyRegistered = this.instanceSuppliers.containsKey(type);
if (replaceExisting || !alreadyRegistered) {
// 確保實例尚未創(chuàng)建
Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created");
// 注冊實例供應者
this.instanceSuppliers.put(type, instanceSupplier);
}
}
}
@Override
public <T> boolean isRegistered(Class<T> type) {
// 檢查給定類型是否已注冊
synchronized (this.instanceSuppliers) {
return this.instanceSuppliers.containsKey(type);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type) {
// 返回已注冊的實例供應者
synchronized (this.instanceSuppliers) {
return (InstanceSupplier<T>) this.instanceSuppliers.get(type);
}
}
@Override
public <T> T get(Class<T> type) throws IllegalStateException {
// 獲取指定類型的實例,如果未注冊則拋出異常
return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered"));
}
@Override
public <T> T getOrElse(Class<T> type, T other) {
// 獲取指定類型的實例,如果未注冊則返回其他提供的實例
return getOrElseSupply(type, () -> other);
}
@Override
public <T> T getOrElseSupply(Class<T> type, Supplier<T> other) {
// 嘗試獲取指定類型的實例,如果未注冊則調(diào)用其他供應者
synchronized (this.instanceSuppliers) {
InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
return (instanceSupplier != null) ? getInstance(type, instanceSupplier) : other.get();
}
}
@Override
public <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X {
// 嘗試獲取指定類型的實例,如果未注冊則拋出由供應者提供的異常
synchronized (this.instanceSuppliers) {
InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
if (instanceSupplier == null) {
throw exceptionSupplier.get();
}
return getInstance(type, instanceSupplier);
}
}
@SuppressWarnings("unchecked")
private <T> T getInstance(Class<T> type, InstanceSupplier<?> instanceSupplier) {
// 獲取實例,如果尚未創(chuàng)建則調(diào)用實例供應者
T instance = (T) this.instances.get(type);
if (instance == null) {
instance = (T) instanceSupplier.get(this);
// 如果作用域為單例,則存儲該實例
if (instanceSupplier.getScope() == Scope.SINGLETON) {
this.instances.put(type, instance);
}
}
return instance;
}
@Override
public void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener) {
// 添加關閉監(jiān)聽器,當 BootstrapContext 關閉時觸發(fā)
this.events.addApplicationListener(listener);
}
/**
* 當 {@link BootstrapContext} 關閉且 {@link ApplicationContext} 已準備好時調(diào)用的方法。
* @param applicationContext 已準備好的上下文
*/
public void close(ConfigurableApplicationContext applicationContext) {
// 發(fā)布 BootstrapContext 關閉事件
this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
}
}三、BootstrapRegistryInitializer
1、作用及觸發(fā)時機
- 回調(diào)接口,用于在
BootstrapRegistry(對象注冊表)使用之前進行初始化 - 作用:應用程序
啟動的早期階段進行必要的初始化和配置
@FunctionalInterface
public interface BootstrapRegistryInitializer {
// 此方法在應用啟動過程中被調(diào)用,允許實現(xiàn)者向注冊表注冊必要的組件或服務。
// 注冊的組件隨后可以在應用上下文中訪問。實現(xiàn)者應僅注冊應用所需的類型。
void initialize(BootstrapRegistry registry);
} 引導注冊組件初始化器BootstrapRegistryInitializer在SpringApplication的構造方法中通過查找META-INF/spring.factories文件進行加載,然后在引導上下文實例創(chuàng)建完成后,遍歷并調(diào)用所有BootstrapRegistryInitializer#initialize方法。

普通SpringBoot項目沒有其實現(xiàn),找了個SpringCloud項目看了下,有兩個

2、示例
自定義BootstrapRegistryInitializer
public class MyBootstrapRegistryInitializer implements BootstrapRegistryInitializer {
@Override
public void initialize(BootstrapRegistry registry) {
// 注冊一個自定義服務
registry.register(MyCustomService.class, context -> new MyCustomService());
System.out.println("MyBootstrapRegistryInitializer已注冊");
}
}
class MyCustomService {
}在META-INF/spring.factories文件中添加對自定義初始化器的配置
org.springframework.boot.BootstrapRegistryInitializer=com.xc.config.MyBootstrapRegistryInitializer
啟動服務

ps:BootstrapRegistryInitializer是SpringBoot第一個擴展點(注冊組件)
總結
- 引導上下文DefaultBootstrapContext創(chuàng)建:在
run方法的最初階段被實例化,并通過BootstrapRegistryInitializer(第一個注冊組件擴展點)進行必要的初始化,確保應用啟動時所需的資源和依賴得到妥善管理 BootstrapRegistry的作用:該接口作為對象注冊表,允許在應用啟動早期階段進行組件的注冊和管理,提供了對高成本實例的惰性訪問BootstrapContext的角色:作為引導上下文的只讀訪問接口,它確保注冊的組件能夠安全、可靠地在應用上下文準備好之前被訪問
到此這篇關于SpringBoot啟動流程之引導上下文DefaultBootstrapContext的過程的文章就介紹到這了,更多相關SpringBoot引導上下文DefaultBootstrapContext內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
簡單幾步實現(xiàn)將Spring security4.x升級到5.x
這篇文章主要介紹了簡單幾步實現(xiàn)將Spring security4.x升級到5.x方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
深入研究spring boot集成kafka之spring-kafka底層原理
這篇文章主要深入研究了spring boot集成kafka如何實現(xiàn)spring-kafka的底層原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02
springIOC的使用流程及spring中使用類型轉(zhuǎn)換器的方式
Spring IOC是Spring框架的核心原理之一,它是一種軟件設計模式,用于管理應用程序中的對象依賴關系,這篇文章主要介紹了springIOC的使用流程以及spring中如何使用類型轉(zhuǎn)換器,需要的朋友可以參考下2023-06-06
java傳入時間戳返回LocalDateTime的實現(xiàn)方法
這篇文章主要介紹了java傳入時間戳返回LocalDateTime的實現(xiàn)方法,在Java中將時間戳轉(zhuǎn)換為LocalDateTime時需要注意時區(qū)問題,因為LocalDateTime不包含時區(qū)信息,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-11-11

