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

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

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

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

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

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

