Spring Boot超詳細(xì)分析啟動流程
一、Spring Boot 工程結(jié)構(gòu)
下載Spring Boot工程源碼, 下載地址
模塊代碼結(jié)構(gòu):
比較重要的是Spring-boot、Spring-boot-autoconfigure以及Spring-boot-starters等組件。
工程模塊介紹
spring-boot
spring boot框架核心插件,對其他模塊提供主要支持。內(nèi)部包含所熟知的SpringApplication類, 提供應(yīng)用程序入口, 內(nèi)嵌支持Tomcat、Jetty和Undertow等容器。
spring-boot-actuator
主要用于管理和監(jiān)控應(yīng)用, 支持http、jmx、ssh、telnet等連接管理方式。
包含審計、健康狀態(tài)、數(shù)據(jù)采集等功能。
spring-boot-actuator-autoconfigure
spring-boot-actuator的擴展、為其提供自動化配置功能。
spring-boot-autoconfigure
實現(xiàn)spring-boot工程的自動化配置, 我們常用的@EnableAutoConfiguration標(biāo)注,就是通過此工程實現(xiàn),
觸發(fā)Spring上下文的自動裝配。 設(shè)計目的是減少開發(fā)者的對bean及應(yīng)用組件的管理配置,專注自己的實現(xiàn)。
spring-boot-cli
提供Spring項目相關(guān)的命令行功能,
安裝CLI相關(guān)工具。即可通過spring run hello.groovy
直接運行Groovy腳本, 不過過多的繁瑣配置, 開發(fā)人員只需關(guān)注業(yè)務(wù)邏輯。
spring-boot-dependencies
Spring Boot項目的maven依賴管理工程, 定義管理各組件的版本號,
內(nèi)部沒有具體代碼實現(xiàn)。
spring-boot-devtools
Spring Boot的開發(fā)工具,比如經(jīng)常調(diào)試程序,使用該插件可以支持熱部署,
不需反復(fù)重啟, 提高開發(fā)效率。
spring-boot-docs
Spring Boot的文檔配置工程,設(shè)置文檔格式、樣式、布局等。
spring-boot-parent
Spring Boot的父級工程, 沒有代碼實現(xiàn), 主要通過dependencyManagement管理各子項目的maven組件依賴。
spring-boot-properties-migrator
Spring Boot的配置屬性監(jiān)察功能, 也就是通過監(jiān)聽器觀察指定的屬性KEY, 發(fā)生變化時,符合指定的匹配規(guī)則,將會觸發(fā)監(jiān)聽事件, 執(zhí)行日志或發(fā)送報告等。
spring-boot-starters
它是一個管理工程, 里面包含各種應(yīng)用組件,例如我們常用的spring-boot-starter-web組件, 提供對web服務(wù)支持;spring-boot-starter-data-jdbc組件, 提供的jdbc數(shù)據(jù)源的封裝使用。里面每個組件只存在一個pom文件, 引入第三方依賴, 這樣能簡化配置, 靈活管理, 保障服務(wù)工程的兼容性。
spring-boot-test
里面包含各種模塊及標(biāo)注, 幫助我們方便測試spring boot 應(yīng)用程序。
spring-boot-test-autoconfigure
服務(wù)于spring-boot-test工程,提供自動化配置,便于集成使用。
spring-boot-tools
spring boot工程的管理工具, 比如ant和maven構(gòu)建、文檔配置工具等。
二、Spring Boot 啟動流程
Spring Boot 的整體啟動流程:
三、Spring Boot 啟動流程源碼剖析
1、創(chuàng)建一個Spring Boot 工程
創(chuàng)建spring-boot-startup工程作為源碼研究。
POM依賴:
<!-- Spring Boot Web 依賴組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
2、SpringBootApplication啟動入口
com.mirson.spring.boot.research.startup.ResearchApplication, 啟動類代碼:
@SpringBootApplication @ComponentScan(basePackages = {"com.mirson"}) public class ResearchApplication { public static void main(String[] args) { SpringApplication.run(ResearchApplication.class, args); } }
核心@SpringBootApplication注解
@Target(ElementType.TYPE) // 注解的適用范圍,其中TYPE用于描述類、接口(包括包注解類型)或enum聲明 @Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三個生命周期) @Documented // 表明這個注解應(yīng)該被javadoc記錄 @Inherited // 子類可以繼承該注解 @SpringBootConfiguration // 繼承了Configuration,表示當(dāng)前是注解類 @EnableAutoConfiguration // 開啟springboot的注解功能,springboot的四大神器之一,其借助@import的幫助 @ComponentScan(excludeFilters = { // 掃描路徑設(shè)置,excludeFilters為排除的過濾器,啟動時不加載 @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }
SpringBootConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
打開其內(nèi)部, 實質(zhì)上就是@Configuration注解, 它是負(fù)責(zé)處理JavaConfig配置, 類似之前Spring在XML中定義的各種配置, 通過@Configuration注解, 會自動加載各種基于JavaConfig實現(xiàn)的配置。比如@Bean定義, @Autowire自動裝配等。
EnableAutoConfiguration
Spring Boot 內(nèi)部封裝了很多組件, 比如異步任務(wù), 緩存, 數(shù)據(jù)源等, EnableAutoConfiguration相當(dāng)于是個總開關(guān), 負(fù)責(zé)管理所有組件的自動化配置,它會去掃描當(dāng)前路徑下所有JavaConfig配置,并且通過AutoConfigurationImportSelector, 加載默認(rèn)的自動化配置組件。
ComponentScan
ComponentScan功能其實是自動掃描并加載符合條件的組件, 比如@Service、@Repository、@Component等, 把它們加載到Spring Ioc容器中。Spring Boot 項目默認(rèn)會掃描加載啟動類所在路徑下的所有JavaConfig配置, 通過ComponetScan指定package, 可以自定義路徑掃描。
3、Spring Boot 初始化分析
從SpringApplication的run方法進入:
創(chuàng)建SpringApplication對象, 查看構(gòu)造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 設(shè)置資源加載器 this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 設(shè)置主資源加載器, 優(yōu)先級更高 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 應(yīng)用類型推斷處理, 標(biāo)識應(yīng)用服務(wù)是REACTIVE模式或SERVLET類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 設(shè)置初始化加載器,默認(rèn)有6個, 作用是讀取spring.factories配置, 每個初始加載器實現(xiàn)ApplicationContextInitializer接口 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 設(shè)置監(jiān)聽器, 默認(rèn)有10個, 包含配置文件, 日志, Classpath等監(jiān)聽器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 設(shè)置啟動類信息 this.mainApplicationClass = deduceMainApplicationClass(); }
Initializers初始化加載器
listeners監(jiān)聽器
4、Spring Boot 啟動深入分析
查看SpringApplication的run方法:
public ConfigurableApplicationContext run(String... args) { // ① 創(chuàng)建計時器 StopWatch stopWatch = new StopWatch(); // 計時器開始統(tǒng)計 stopWatch.start(); // ② 定義配置型上下文, 除了具備ApplicationContex, 還擁有生命周期和流屬性 ConfigurableApplicationContext context = null; // ③ 定義異常記錄報告 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // ④ 識別是否Headless模式, 該模式下,系統(tǒng)沒有顯示設(shè)備、鍵盤或鼠標(biāo) configureHeadlessProperty(); // ⑤ 獲取所有Spring Boot 內(nèi)置監(jiān)聽器 SpringApplicationRunListeners listeners = getRunListeners(args); // 啟動所有監(jiān)聽器 listeners.starting(); try { // ⑥ 設(shè)置服務(wù)啟動時接收的參數(shù) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 設(shè)置環(huán)境變量信息, 為監(jiān)聽器和ConfigurationPropertySources初始環(huán)境變量 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // ⑦ 忽略環(huán)境變量中配置的BEAN信息 configureIgnoreBeanInfo(environment); // ⑧ 控制臺打印Banner條, 可以自定義圖片背景等 Banner printedBanner = printBanner(environment); // ⑨ 創(chuàng)建上下文, 根據(jù)不同模式進行創(chuàng)建(SERVLET、REACTIVE) context = createApplicationContext(); // ⑩ 獲取Spring Factory加載時的異常報告 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // ? Spring上下文加載預(yù)處理 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // ? Spring 上下文加載處理 refreshContext(context); // ? Spring 上下文加載完畢的后置處理, 內(nèi)部暫為空實現(xiàn) afterRefresh(context, applicationArguments); // 停止時間計時器 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // ? 啟動監(jiān)聽器 listeners.started(context); // ? Spring Boot 容器啟動完畢后, 調(diào)用ApplicationRunner的run方法, 處理自定義邏輯 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 執(zhí)行監(jiān)聽器 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
① 創(chuàng)建StopWatch,Spring 提供的計時器,統(tǒng)計Spring Boot應(yīng)用啟動時長信息。
② 定義Spring上下文, 并下面第九處執(zhí)行賦值。
③ 定義異常報告集合, 收集Spring Factory的初始化異常信息。
④ 配置headless信息, 該模式下,系統(tǒng)沒有顯示設(shè)備、鍵盤或鼠標(biāo)。
⑤ 獲取啟動監(jiān)聽器,為一個集合,可以包含多個監(jiān)聽, 實現(xiàn)SpringApplicationRunListener接口,
該接口定義了容器啟動的完整生命周期, 如啟動、運行、環(huán)境準(zhǔn)備、上下文準(zhǔn)備、加載等。EventPublishingRunListener就是一個具體實現(xiàn),
將SpringApplicationRunListener接口監(jiān)聽到的事件, 轉(zhuǎn)化為SpringApplicationEvent事件, 注冊并廣播到所有監(jiān)聽器下面。
⑥ 服務(wù)配置處理,創(chuàng)建applicationArguments, 為應(yīng)用的命令行參數(shù), 啟動程序時可以指定, 比如—debug等; prepareEnvrionment根據(jù)參數(shù)加載屬性配置, 包含自定義的屬性配置,像我們常見的application.yml都會加載進去, 默認(rèn)會加載7個類型的配置到envrionment中。
⑦ configureIgnoreBeanInfo作用是配置是否忽略beaninfo處理, 默認(rèn)是為true,會自動忽略不檢索加載工程中的beaninfo類信息。
⑧ printBanner打印Spring Boot啟動的Banner條, 通過bannerMode屬性控制是否打印,內(nèi)部有g(shù)etImageBanner和getTextBanner實現(xiàn), 可以支持圖像與文本。
⑨ 容器創(chuàng)建, 通過createApplicationContext方法創(chuàng)建Spring容器, 會根據(jù)應(yīng)用類型, 加載不同上下文處理類。Web Servlet類型會加載AnnotationConfigServletWebServerApplicationContext; Reactive類型會 加載AnnotationConfigReactiveWebServerApplicationContext。都是繼承ServletWebServerApplicationContext類, 實現(xiàn)ConfigurableWebApplicationContext接口。 在不指 定應(yīng)用類型的i情況下,默認(rèn)通過AnnotationConfigApplicationContext類處理上下文,該類是繼承GenericApplicationContext,實現(xiàn)ConfigurableApplicationContext接口。
⑩ 初始化exceptionReports集合, 通過SpringFactory加載,
內(nèi)置有19個異常分析器, 常見的BeanDefinition定義錯誤、NoUnique唯一性約束、PortInUse端口占用等異常都是通過這些分析器處理拋出。
?上下文加載預(yù)處理,prepareContext方法內(nèi)部實現(xiàn):
設(shè)置context的environment環(huán)境配置屬性,通過applyInitializer初始化ApplicationContextInitializer。通知監(jiān)聽器告訴上下文預(yù)處理工作完成。 接下來創(chuàng)建所熟知的beanFactory容器管理工廠, 加載BeanDefinition, 最后通知監(jiān)聽器,加載完成。
? 刷新啟動Spring容器, 調(diào)用refreshConext方法。深入內(nèi)部,可以看到核心處理流程:
@Override public void refresh() throws BeansException, IllegalStateException { // 設(shè)置同步鎖, 防止啟動關(guān)閉造成的資源爭搶 synchronized (this.startupShutdownMonitor) { // Spring 上下文預(yù)加載處理 prepareRefresh(); // 刷新并返回bean工廠信息 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 預(yù)加載bean工廠信息, 如設(shè)定classloader加載器, 接口依賴等。 prepareBeanFactory(beanFactory); try { // bean工廠處理, 設(shè)置上下文的BeanPostProcessor和ignoreDependencyInterface等信息 postProcessBeanFactory(beanFactory); // 調(diào)用所有在BeanFactory中已注冊的beanFactoryPostProcessor。beanFactoryPostProcessor 是BeanFactory的后置處理器, 對BeanDefinition對象修改,如我們在XML或標(biāo)注配置的bean定義信息 invokeBeanFactoryPostProcessors(beanFactory); // 通過委派機制注冊Bean創(chuàng)建時的Bean Processors對象 。BeanPostProcessor是Bean后置處理器, 負(fù)責(zé)對Bean對象修改, 比如實現(xiàn)InitializingBean接口的Bean, 通過afterPropertiesSet方法設(shè)置修改 registerBeanPostProcessors(beanFactory); // 初始化message source , 比如我們常見的國際化信息處理。 initMessageSource(); // 初始化事件監(jiān)聽器并進行廣播。可以將ApplicationListener監(jiān)聽的事件發(fā)送至相應(yīng)監(jiān)聽器做處理。 initApplicationEventMulticaster(); // 實現(xiàn)其他的指定Bean或容器處理, 比如GenericApplicationContext, 可以實現(xiàn)一些自定義UI或Theme。 onRefresh(); // 注冊所有實現(xiàn)ApplicationListener接口的監(jiān)聽器, 廣播處理相應(yīng)事件。 registerListeners(); // 創(chuàng)建所有非懶加載方式定義單例類。 finishBeanFactoryInitialization(beanFactory); // 完成容器的加載處理, 善后工作, 清除資源緩存, 發(fā)布完成事件等。 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 銷毀bean信息 destroyBeans(); // 復(fù)位 'active'激活標(biāo)志. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // 清除基本緩存信息, 比如方法屬性聲明緩存、標(biāo)注緩存等。 resetCommonCaches(); } } }
? 為Spring 容器初始化的后置處理方法,預(yù)置功能,內(nèi)部為空實現(xiàn)。
? Spring容器啟動完成后, 通知SpringApplicationListener監(jiān)聽數(shù)組,
觸發(fā)容器加載完成started事件, 執(zhí)行監(jiān)聽邏輯。
? 再調(diào)用運行器, 檢查ApplicationContext中有無定義,調(diào)用ApplicationRunner、CommandLineRunner接口的run方法。
最后, 調(diào)用所有定義的SpringApplicationListener監(jiān)聽器,觸發(fā)容器正常運行Running事件, 執(zhí)行監(jiān)聽邏輯。
四、總結(jié)
Spring Boot 能夠極為簡化的開發(fā)與配置, 從啟動流程的研究分析, Spring Boot 做了大量的封裝與自動化處理, 通過掃描Spring Factory 能夠加載各種自動化組件, 同時內(nèi)置了監(jiān)聽器與各種事件, 以及ApplicationRunner啟動器, 具有較強的靈活性與擴展性, Spring Boot 內(nèi)部封裝簡潔, 邏輯清晰,沒有過多的冗余代碼, 能夠起到很好的借鑒學(xué)習(xí)作用。
到此這篇關(guān)于Spring Boot超詳細(xì)分析啟動流程的文章就介紹到這了,更多相關(guān)Spring Boot啟動流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Security權(quán)限管理小結(jié)
SpringSecurity是一個權(quán)限管理框架,核心是認(rèn)證和授權(quán),前面已經(jīng)系統(tǒng)的給大家介紹過了認(rèn)證的實現(xiàn)和源碼分析,本文重點來介紹下權(quán)限管理,需要的朋友可以參考下2022-08-08Java listener簡介_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了Java listener簡介,可以用于統(tǒng)計用戶在線人數(shù)等,有興趣的可以了解一下2017-07-07SpringBoot項目打包發(fā)布到外部tomcat(出現(xiàn)各種異常的解決)
這篇文章主要介紹了SpringBoot項目打包發(fā)布到外部tomcat(出現(xiàn)各種異常的解決),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09java如何實現(xiàn)post請求webservice服務(wù)端
這篇文章主要介紹了java如何實現(xiàn)post請求webservice服務(wù)端,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03SpringBoot結(jié)合JWT實現(xiàn)用戶登錄、注冊、鑒權(quán)
用戶登錄、注冊及鑒權(quán)是我們基本所有系統(tǒng)必備的,也是很核心重要的一塊,本文主要介紹了SpringBoot結(jié)合JWT實現(xiàn)用戶登錄、注冊、鑒權(quán),具有一定的參考價值,感興趣的小伙伴們可以參考一下2023-05-05