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)注冊組件初始化器集合
(查詢spring.factories文件,沒有找到BootstrapRegistryInitializer的實(shí)現(xiàn)類)- 調(diào)用初始化器的initialize方法,參數(shù)為bootstrapContext,也就是說每個(gè)初始化器都會(huì)對bootstrapContext進(jìn)行必要的設(shè)置和準(zhǔn)備(
啟動(dòng)時(shí)需要的資源和依賴
) - 本方法是在run方法最開始調(diào)用的,也就是說引導(dǎo)注冊組件初始化器組件的
執(zhí)行時(shí)機(jī)最早
了
主要內(nèi)容就是實(shí)例化DefaultBootstrapContext
以及遍歷BootstrapRegistryInitializer
集合調(diào)用initialize,下面詳細(xì)介紹下這兩個(gè)類的作用。
// SpringApplication類屬性方法 // 引導(dǎo)注冊初始化器 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 都可以對 bootstrapContext 進(jìn)行相應(yīng)的配置, // 為應(yīng)用程序的啟動(dòng)過程準(zhǔn)備所需的資源。 this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); // 返回已完成初始化的 DefaultBootstrapContext 對象 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è)簡單的對象注冊表
,在啟動(dòng)和處理環(huán)境配置期間可用,直到ApplicationContext
準(zhǔn)備好為止。提供對單例的惰性訪問,這些單例的創(chuàng)建成本可能很高,或者需要在ApplicationContext可用之前共享。
注冊表使用Class
作為鍵,這意味著只能存儲(chǔ)給定類型的單個(gè)實(shí)例
。
addCloseListener(ApplicationListener)方法可用于添加監(jiān)聽器,當(dāng)BootstrapContext關(guān)閉且ApplicationContext已準(zhǔn)備好時(shí),該監(jiān)聽器可以執(zhí)行某些操作。例如,實(shí)例可以選擇將自身注冊為常規(guī)SpringBean,以便可供應(yīng)用程序使用。
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},當(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é):用于注冊引導(dǎo)階段的組件,在應(yīng)用啟動(dòng)時(shí)通過register方法動(dòng)態(tài)添加對象
2、BootstrapContext接口
一個(gè)簡單的引導(dǎo)上下文
,在啟動(dòng)和處理環(huán)境配置期間可用,直到ApplicationContext
準(zhǔn)備好為止。提供對單例的惰性訪問,這些單例的創(chuàng)建成本可能很高,或者需要在ApplicationContext可用之前共享。
public interface BootstrapContext { // 如果類型已注冊,則從上下文中返回實(shí)例。如果之前未訪問過該實(shí)例,則會(huì)創(chuàng)建該實(shí)例 <T> T get(Class<T> type) throws IllegalStateException; // 如果類型已注冊,則返回上下文中的實(shí)例。如果尚未訪問該實(shí)例,則將創(chuàng)建它。 // 如果類型未注冊,則返回指定的替代實(shí)例。 <T> T getOrElse(Class<T> type, T other); // 如果類型已注冊,則返回上下文中的實(shí)例。如果尚未訪問該實(shí)例,則將創(chuàng)建它。 // 如果類型未注冊,則使用指定的供應(yīng)者提供的實(shí)例。 <T> T getOrElseSupply(Class<T> type, Supplier<T> other); // 如果類型已注冊,則返回上下文中的實(shí)例。如果尚未訪問該實(shí)例,則將創(chuàng)建它。 // 如果類型未注冊,則拋出由供應(yīng)者提供的異常。 <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X; // 返回給定類型是否存在注冊 <T> boolean isRegistered(Class<T> type); }
總結(jié):用于提供對引導(dǎo)階段注冊組件的只讀訪問,一旦BootstrapRegistry注冊完成并構(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í)例
,提供了注冊、獲取、關(guān)閉監(jiān)聽等功能。以下是主要功能的總結(jié):
1.實(shí)例供應(yīng)者管理:
- 使用
Map<Class<?>, InstanceSupplier<?>> instanceSuppliers
來存儲(chǔ)類型到實(shí)例供應(yīng)者的映射,可以通過register
和registerIfAbsent
方法來注冊實(shí)例供應(yīng)者 register
方法支持覆蓋現(xiàn)有的實(shí)例供應(yīng)者,而registerIfAbsent
則僅在類型未注冊的情況下注冊實(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) { // 注冊特定類型的實(shí)例供應(yīng)者 register(type, instanceSupplier, true); } @Override public <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier) { // 如果尚未注冊,則注冊特定類型的實(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) { // 檢查類型是否已注冊 boolean alreadyRegistered = this.instanceSuppliers.containsKey(type); if (replaceExisting || !alreadyRegistered) { // 確保實(shí)例尚未創(chuàng)建 Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created"); // 注冊實(shí)例供應(yīng)者 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) { // 返回已注冊的實(shí)例供應(yīng)者 synchronized (this.instanceSuppliers) { return (InstanceSupplier<T>) this.instanceSuppliers.get(type); } } @Override public <T> T get(Class<T> type) throws IllegalStateException { // 獲取指定類型的實(shí)例,如果未注冊則拋出異常 return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered")); } @Override public <T> T getOrElse(Class<T> type, T other) { // 獲取指定類型的實(shí)例,如果未注冊則返回其他提供的實(shí)例 return getOrElseSupply(type, () -> other); } @Override public <T> T getOrElseSupply(Class<T> type, Supplier<T> other) { // 嘗試獲取指定類型的實(shí)例,如果未注冊則調(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í)例,如果未注冊則拋出由供應(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(對象注冊表)
使用之前進(jìn)行初始化 - 作用:應(yīng)用程序
啟動(dòng)的早期階段
進(jìn)行必要的初始化
和配置
@FunctionalInterface public interface BootstrapRegistryInitializer { // 此方法在應(yīng)用啟動(dòng)過程中被調(diào)用,允許實(shí)現(xiàn)者向注冊表注冊必要的組件或服務(wù)。 // 注冊的組件隨后可以在應(yīng)用上下文中訪問。實(shí)現(xiàn)者應(yīng)僅注冊應(yīng)用所需的類型。 void initialize(BootstrapRegistry registry); }
引導(dǎo)注冊組件初始化器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) { // 注冊一個(gè)自定義服務(wù) 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
啟動(dòng)服務(wù)
ps:BootstrapRegistryInitializer是SpringBoot第一個(gè)擴(kuò)展點(diǎn)(注冊組件)
總結(jié)
- 引導(dǎo)上下文DefaultBootstrapContext創(chuàng)建:在
run
方法的最初階段被實(shí)例化,并通過BootstrapRegistryInitializer(第一個(gè)注冊組件擴(kuò)展點(diǎn))
進(jìn)行必要的初始化,確保應(yīng)用啟動(dòng)時(shí)所需的資源和依賴得到妥善管理 BootstrapRegistry
的作用:該接口作為對象注冊表,允許在應(yīng)用啟動(dòng)早期階段進(jìn)行組件的注冊
和管理,提供了對高成本實(shí)例的惰性訪問BootstrapContext
的角色:作為引導(dǎo)上下文的只讀訪問接口,它確保注冊的組件能夠安全、可靠地在應(yīng)用上下文準(zhǔn)備好之前被訪問
到此這篇關(guān)于SpringBoot啟動(dòng)流程之引導(dǎo)上下文DefaultBootstrapContext的過程的文章就介紹到這了,更多相關(guān)SpringBoot引導(dǎo)上下文DefaultBootstrapContext內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡單幾步實(shí)現(xiàn)將Spring security4.x升級到5.x
這篇文章主要介紹了簡單幾步實(shí)現(xiàn)將Spring security4.x升級到5.x方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08深入研究spring boot集成kafka之spring-kafka底層原理
這篇文章主要深入研究了spring boot集成kafka如何實(shí)現(xiàn)spring-kafka的底層原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02springIOC的使用流程及spring中使用類型轉(zhuǎn)換器的方式
Spring IOC是Spring框架的核心原理之一,它是一種軟件設(shè)計(jì)模式,用于管理應(yīng)用程序中的對象依賴關(guān)系,這篇文章主要介紹了springIOC的使用流程以及spring中如何使用類型轉(zhuǎn)換器,需要的朋友可以參考下2023-06-06MapReduce實(shí)現(xiàn)TopN效果示例解析
這篇文章主要為大家介紹了MapReduce實(shí)現(xiàn)TopN效果示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07java傳入時(shí)間戳返回LocalDateTime的實(shí)現(xiàn)方法
這篇文章主要介紹了java傳入時(shí)間戳返回LocalDateTime的實(shí)現(xiàn)方法,在Java中將時(shí)間戳轉(zhuǎn)換為LocalDateTime時(shí)需要注意時(shí)區(qū)問題,因?yàn)長ocalDateTime不包含時(shí)區(qū)信息,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11