SpringBoot的啟動(dòng)過程源碼詳細(xì)分析
SpringBoot啟動(dòng)過程源碼
程序的入口:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
最終會(huì)調(diào)用SpringApplication的構(gòu)造方法。
構(gòu)造SpringApplication對(duì)象
- 推測web應(yīng)用類型
- 獲取BootstrapRegistryInitializer對(duì)象
- 獲取ApplicationContextInitializer對(duì)象
- 獲取ApplicationListener對(duì)象
- 推測出Main類(main()方法所在的類)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 推測web應(yīng)用類型 // 如果項(xiàng)目依賴中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么應(yīng)用類型為WebApplicationType.REACTIVE // 如果項(xiàng)目依賴中不存在org.springframework.web.reactive.DispatcherHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么應(yīng)用類型為WebApplicationType.NONE // 否則,應(yīng)用類型為WebApplicationType.SERVLET this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 獲取BootstrapRegistryInitializer對(duì)象 // 從"META-INF/spring.factories"中讀取key為BootstrapRegistryInitializer類型的擴(kuò)展點(diǎn),并實(shí)例化出對(duì)應(yīng)擴(kuò)展點(diǎn)對(duì)象 // BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry // 下面的DefaultBootstrapContext對(duì)象就是一個(gè)BootstrapRegistry,可以用來注冊(cè)一些對(duì)象,這些對(duì)象可以用在從SpringBoot啟動(dòng)到Spring容器初始化完成的過程中 // 我的理解:沒有Spring容器之前就利用BootstrapRegistry來共享一些對(duì)象,有了Spring容器之后就利用Spring容器來共享一些對(duì)象 this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 獲取ApplicationContextInitializer對(duì)象 // 從"META-INF/spring.factories"中讀取key為ApplicationContextInitializer類型的擴(kuò)展點(diǎn),并實(shí)例化出對(duì)應(yīng)擴(kuò)展點(diǎn)對(duì)象 // 顧名思義,ApplicationContextInitializer是用來初始化Spring容器ApplicationContext對(duì)象的,比如可以利用ApplicationContextInitializer來向Spring容器中添加ApplicationListener setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 獲取ApplicationListener對(duì)象 // 從"META-INF/spring.factories"中讀取key為ApplicationListener類型的擴(kuò)展點(diǎn),并實(shí)例化出對(duì)應(yīng)擴(kuò)展點(diǎn)對(duì)象 // ApplicationListener是Spring中的監(jiān)聽器,并不是SpringBoot中的新概念,不多解釋了 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推測出Main類(main()方法所在的類) // 沒什么具體的作用,邏輯是根據(jù)當(dāng)前線程的調(diào)用棧來判斷main()方法在哪個(gè)類,哪個(gè)類就是Main類 this.mainApplicationClass = deduceMainApplicationClass(); }
run(String… args)方法
run()主要負(fù)責(zé)Spring的啟動(dòng):
org.springframework.boot.SpringApplication#run(java.lang.String…)
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); // 創(chuàng)建DefaultBootstrapContext對(duì)象 // 用BootstrapRegistryInitializer初始化DefaultBootstrapContext對(duì)象 // DefaultBootstrapContext用來在spring容器創(chuàng)建之前共享對(duì)象 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 從"META-INF/spring.factories"中讀取key為SpringApplicationRunListener類型的擴(kuò)展點(diǎn),并實(shí)例化出對(duì)應(yīng)擴(kuò)展點(diǎn)對(duì)象 SpringApplicationRunListeners listeners = getRunListeners(args); // 觸發(fā)SpringApplicationRunListener的starting() // 默認(rèn)情況下SpringBoot提供了一個(gè)EventPublishingRunListener,發(fā)布ApplicationStartingEvent事件 listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // Environment對(duì)象表示環(huán)境變量,該對(duì)象內(nèi)部主要包含了 // 1. 當(dāng)前操作系統(tǒng)的環(huán)境變量 // 2. JVM的一些配置信息 // 3. -D方式所配置的JVM環(huán)境變量 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); // 打印banner Banner printedBanner = printBanner(environment); // 根據(jù)webApplicationType不同創(chuàng)建不同的ApplicationContext context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // refreshContext前的準(zhǔn)備工作,例如注入一些配置Bean prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 調(diào)用applicationContext.refresh(); refreshContext(context); // 空方法 afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { // 打印啟動(dòng)時(shí)間日志 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } // 發(fā)布ApplicationStartedEvent事件 // 發(fā)布AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為LivenessState.CORRECT listeners.started(context, timeTakenToStartup); // 調(diào)用ApplicationRunner.run // 調(diào)用CommandLineRunner.run callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); // 發(fā)布ApplicationReadyEvent事件 // 發(fā)布AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為ReadinessState.ACCEPTING_TRAFFIC listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
創(chuàng)建Environment對(duì)象
Environment對(duì)象表示環(huán)境變量,該對(duì)象內(nèi)部主要包含了:
- 當(dāng)前操作系統(tǒng)的環(huán)境變量
- JVM的一些配置信息
- -D方式所配置的JVM環(huán)境變量
org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create and configure the environment // 創(chuàng)建環(huán)境變量,一般是ApplicationServletEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 向environment中添加defaultProperties // 將命令行的參數(shù)封裝為SimpleCommandLinePropertySource,加入environment configureEnvironment(environment, applicationArguments.getSourceArgs()); // 添加ConfigurationPropertySourcesPropertySource到environment ConfigurationPropertySources.attach(environment); // 默認(rèn)情況下會(huì)利用EventPublishingRunListener發(fā)布ApplicationEnvironmentPreparedEvent事件 // 比如默認(rèn)情況下會(huì)有一個(gè)EnvironmentPostProcessorApplicationListener來消費(fèi)這個(gè)事件 // 而這個(gè)ApplicationListener接收到這個(gè)事件之后,就會(huì)解析application.properties、application.yml文件,并添加到Environment對(duì)象中去。 /** * @see EnvironmentPostProcessorApplicationListener */ listeners.environmentPrepared(bootstrapContext, environment); // 將DefaultPropertiesPropertySource移到最后 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = convertEnvironment(environment); } // 為什么又調(diào)用一次? ConfigurationPropertySources.attach(environment); return environment; }
創(chuàng)建Spring容器對(duì)象(ApplicationContext)
會(huì)利用ApplicationContextFactory.DEFAULT,根據(jù)應(yīng)用類型創(chuàng)建對(duì)應(yīng)的Spring容器。
ApplicationContextFactory.DEFAULT為:
ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { /** * org.springframework.boot.ApplicationContextFactory=\ * org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\ * org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory */ for (ApplicationContextFactory candidate : SpringFactoriesLoader .loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) { // 創(chuàng)建的是AnnotationConfigServletWebServerApplicationContext ConfigurableApplicationContext context = candidate.create(webApplicationType); if (context != null) { return context; } } return new AnnotationConfigApplicationContext(); } catch (Exception ex) { throw new IllegalStateException("Unable create a default ApplicationContext instance, " + "you may need a custom ApplicationContextFactory", ex); } };
所以:
- 應(yīng)用類型為SERVLET,則對(duì)應(yīng)AnnotationConfigServletWebServerApplicationContext
- 應(yīng)用類型為REACTIVE,則對(duì)應(yīng)AnnotationConfigReactiveWebServerApplicationContext
- 應(yīng)用類型為普通類型,則對(duì)應(yīng)AnnotationConfigApplicationContext
利用ApplicationContextInitializer初始化Spring容器對(duì)象
默認(rèn)情況下SpringBoot提供了多個(gè)ApplicationContextInitializer,其中比較重要的有ConditionEvaluationReportLoggingListener,別看到它的名字叫XXXListener,但是它確實(shí)是實(shí)現(xiàn)了ApplicationContextInitializer接口的。
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener#initialize
@Override public void initialize(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; applicationContext.addApplicationListener(new ConditionEvaluationReportListener()); if (applicationContext instanceof GenericApplicationContext) { // Get the report early in case the context fails to load this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory()); } }
在它的initialize()方法中會(huì):
- 將Spring容器賦值給它的applicationContext屬性
- 并且往Spring容器中添加一個(gè)ConditionEvaluationReportListener(ConditionEvaluationReportLoggingListener的內(nèi)部類),它是一個(gè)ApplicationListener
- 并生成一個(gè)ConditionEvaluationReport對(duì)象賦值給它的report屬性
ConditionEvaluationReportListener會(huì)負(fù)責(zé)接收ContextRefreshedEvent事件,也就是Spring容器一旦啟動(dòng)完畢就會(huì)觸發(fā)ContextRefreshedEvent,ConditionEvaluationReportListener就會(huì)打印自動(dòng)配置類的條件評(píng)估報(bào)告。
觸發(fā)SpringApplicationRunListener的contextPrepared()
默認(rèn)情況下會(huì)利用EventPublishingRunListener發(fā)布一個(gè)ApplicationContextInitializedEvent事件,默認(rèn)情況下暫時(shí)沒有ApplicationListener消費(fèi)了這個(gè)事件
調(diào)用DefaultBootstrapContext對(duì)象的close()
沒什么特殊的,忽略
將啟動(dòng)類作為配置類注冊(cè)到Spring容器中(load()方法)
將SpringApplication.run(MyApplication.class);中傳入進(jìn)來的類,比如MyApplication.class,作為Spring容器的配置類
觸發(fā)SpringApplicationRunListener的contextLoaded()
默認(rèn)情況下會(huì)利用EventPublishingRunListener發(fā)布一個(gè)ApplicationPreparedEvent事件
刷新Spring容器
調(diào)用Spring容器的refresh()方法,相當(dāng)于執(zhí)行了這樣一個(gè)流程:
AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext(); applicationContext .register(MyApplication.class) applicationContext .refresh()
觸發(fā)SpringApplicationRunListener的started()
發(fā)布ApplicationStartedEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為LivenessState.CORRECT
LivenessState枚舉有兩個(gè)值:
- CORRECT:表示當(dāng)前應(yīng)用正常運(yùn)行中
- BROKEN:表示當(dāng)前應(yīng)用還在運(yùn)行,但是內(nèi)部出現(xiàn)問題,暫時(shí)還沒發(fā)現(xiàn)哪里用到了
調(diào)用ApplicationRunner和CommandLineRunner
- 獲取Spring容器中的ApplicationRunner類型的Bean
- 獲取Spring容器中的CommandLineRunner類型的Bean
- 執(zhí)行它們的run()
觸發(fā)SpringApplicationRunListener的ready()
發(fā)布ApplicationReadyEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為ReadinessState.ACCEPTING_TRAFFIC
ReadinessState枚舉有兩個(gè)值:
ACCEPTING_TRAFFIC:表示當(dāng)前應(yīng)用準(zhǔn)備接收請(qǐng)求
REFUSING_TRAFFIC:表示當(dāng)前應(yīng)用拒絕接收請(qǐng)求,比如Tomcat關(guān)閉時(shí),就會(huì)發(fā)布AvailabilityChangeEvent事件,并且狀態(tài)為REFUSING_TRAFFIC
上述過程拋異常了就觸發(fā)SpringApplicationRunListener的failed()
發(fā)布ApplicationFailedEvent事件。
到此這篇關(guān)于SpringBoot的啟動(dòng)過程源碼詳細(xì)分析的文章就介紹到這了,更多相關(guān)SpringBoot啟動(dòng)過程源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java網(wǎng)絡(luò)編程之基于TCP協(xié)議
本文主要將Java基于TCP的網(wǎng)絡(luò)編程主要分解成5個(gè)功能:功能分解1:單向通信功能分解,2:雙向通信功能分解,3:對(duì)象流傳送功能分解,4:加入完整的處理異常方式功能分解,5:多線程接收用戶請(qǐng)求,需要的朋友可以參考下2021-05-05手把手帶你分析SpringBoot自動(dòng)裝配完成了Ribbon哪些核心操作
這篇文章主要介紹了詳解Spring Boot自動(dòng)裝配Ribbon哪些核心操作的哪些操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08Maven忽略單元測試及打包到Nexus的實(shí)現(xiàn)
我們的工程在打包發(fā)布時(shí)候,通常都需要忽略單元測試,以免因環(huán)境原因,無法通過單元測試而影響發(fā)布,本文主要介紹了Maven忽略單元測試及打包到Nexus的實(shí)現(xiàn),感興趣的可以了解一下2024-04-04使用Spring Initializr創(chuàng)建Spring Boot項(xiàng)目沒有JDK1.8的解決辦法
很久沒創(chuàng)建springboot項(xiàng)目,今天使用idea的Spring Initializr 創(chuàng)建 Spring Boot項(xiàng)目時(shí),發(fā)現(xiàn)java版本里,無法選擇jdk1.8,只有17、21、22,所以本文介紹了使用Spring Initializr創(chuàng)建Spring Boot項(xiàng)目沒有JDK1.8的解決辦法,需要的朋友可以參考下2024-06-06intellij idea如何將web項(xiàng)目打成war包的實(shí)現(xiàn)
這篇文章主要介紹了intellij idea如何將web項(xiàng)目打成war包的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Spring Security OAuth 自定義授權(quán)方式實(shí)現(xiàn)手機(jī)驗(yàn)證碼
這篇文章主要介紹了Spring Security OAuth 自定義授權(quán)方式實(shí)現(xiàn)手機(jī)驗(yàn)證碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02淺談JAVA工作流的優(yōu)雅實(shí)現(xiàn)方式
這篇文章主要介紹了淺談JAVA工作流的優(yōu)雅實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11springBoot 打war包 程序包c(diǎn)om.sun.istack.internal不存在的問題及解決方案
這篇文章主要介紹了springBoot 打war包 程序包c(diǎn)om.sun.istack.internal不存在的問題及解決方案,親測試過可以,需要的朋友可以參考下2018-07-07Java利用EasyExcel實(shí)現(xiàn)導(dǎo)出導(dǎo)入功能的示例代碼
EasyExcel是一個(gè)基于Java的、快速、簡潔、解決大文件內(nèi)存溢出的Excel處理工具。本文廢話不多說,直接上手試試,用代碼試試EasyExcel是否真的那么好用2022-11-11