說明:spring boot版本 2.0.6.RELEASE
思緒
首先,大家認識spring boot是從@SpringBootApplication注解和org.springframework.boot.SpringApplication.run(Class<?>, String...)開始的,那么我們就從這兩個方向入手一探究竟。
@SpringBootApplication注解
先來看下@SpringBootApplication的申明,如下圖:
核心也就是@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan這三個注解的組合。
@SpringBootConfiguration如下,即標明被@SpringBootApplication注解標注的類本身也是spring的java config類。
@ComponentScan,配置自動掃描功能,等同xml配置的<context:component-scan>。可以指定basePackages或者basePackageClasses標明掃描的根路徑,如果不設置的話默認掃描@ComponentScan注解所在類的同級類和同級目錄下的所有類,也就是@SpringBootApplication所在的類,所以一般spring boot項目的入口類會放在頂層目錄中,這樣就可以自動掃描到項目中所有的spring配置類,該注解的處理類為org.springframework.context.annotation.ComponentScanAnnotationParser.parse(AnnotationAttributes, String),從ApplicationContext中refresh的invokeBeanFactoryPostProcessors調用過來的,調用棧如下圖所示:
@EnableAutoConfiguration,自動配置類,是簡化spring配置的核心,spring一貫的風格凡是enable開頭的注解都會配備一個@Import注解來引入一個ImportSelector。這里的@Import如下圖所示:
掃盲:@Import注解作用是將values配置的class加入springIOC容器中:
如果是@Configuration的配置類,則將對應的java config產生的bean納入spring管理;
如果是普通java類,則將該類實例化並納入spring管理;
重點看下org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.selectImports(AnnotationMetadata)方法,返回一個string數組,內容為需要加載的類限定名。
核心是通過spring的FactoriesLoader機制加載以EnableAutoConfiguration注解全限定類名為key的class。
核心在org.springframework.core.io.support.SpringFactoriesLoader.loadSpringFactories(ClassLoader)方法中,如下圖所示,其實就是在classpath下查找所有

下圖就是其中一個示例:
spring.factories相關內容如下:
以直觀的web容器自動配置為例:
其中的ServletWebServerFactoryAutoConfiguration和ReactiveWebServerFactoryAutoConfiguration即為根據ConditionalOnWebApplication應用類型來加載對應的webServerFactory,classpath下的自動配置webServer:
備注:ServletWebServer和ReactiveWebServer是兩個路子,根據WebApplicationType走不同的路。
拿ReactiveWebServerFactoryAutoConfiguration來說,如下圖所示,@Import了ConditionalOnWebApplication.Type.REACTIVE應用類型可能用到的WebServerFactory。
內部再根據對應條件創建對應的WebServerFactory。
最終org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.getWebServerFactory()以及
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory()從BeanFactory獲取對應的WebServerFactory
串起來的話是在org.springframework.context.support.AbstractApplicationContext.refresh()中的onRefresh()[子類]的createWebServer創建server,finishRefresh()[子類]啟動server。
好了,到這里spring boot的@SpringBootApplication注解就分析完了。
SpringApplication.run分析
方法入口先創建SpringApplication對象,然后調用實例run方法,如下圖。
先看看構造方法都做了些什么:
1. 推斷webApplicationType
2. 通過SpringFactoriesLoader機制獲取並創建ApplicationContextInitializer和ApplicationListener
3. 推斷應用主class(mainApplicationClass)
這些是用於擴展用的,在整個啟動過程中插入對應的擴展點,具體邏輯要看實現這些接口的具體類里面的邏輯。
我們先來看主線流程,分析源碼要先把主線流程邏輯拉通,再看各個點的邏輯,由面及點,這樣才不會讓自己陷入其中,不明所以。
如下圖所示,主線其實就是創建了一個context,prepareContext以及refresh該context。
創建context
根據之前構造方法中推動出的應用類型創建對應的context,如下圖所示。
我們拿reactive類型來說,對應的context為org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
繼承關系如下:
創建沒什么,就這么多,重點是下面講的prepareContext
prepareContext
里面重點是加載beanDefinitions,而原來spring框架里面是在org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()創建完beanFactory后loadBeanDefinitions,詳見下面refreshContext的分析。
refreshContext
最終調用的是org.springframework.context.support.AbstractApplicationContext.refresh()這個方法,了解spring的人應該很清楚這個方法,我之前的spring啟動過程源碼分析文章也講得很清楚了。方法內容如下:
重點看下obtainFreshBeanFactory方法,如下圖所示:
里面重點是refreshBeanFactory方法,我們先看下繼承關系:
而spring原有的context繼承自上面那個類,如下圖所示:
所以refreshBeanFactory在以前spring本身的代碼中,調用的是org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()方法,如下圖所示,先銷毀再創建新的beanfactory,同時加載beanDefinitions,當然,第一創建beanFactory也是在這里。其實從類名很清楚就可以看出,這個基類是支持動態刷新context的:
而spring boot繼承的基類只是setSerializationId,如下圖:
spring boot創建beanFactory是在org.springframework.context.support.GenericApplicationContext.GenericApplicationContext()構造方法中。
至此,spring boot 創建context已經完成,在子類org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.onRefresh()方法中創建webServer,
在finishRefresh中啟動webServer:
子類重寫的這兩個方法在父類org.springframework.context.support.AbstractApplicationContext.refresh()方法中調用的,有沒有get到?這下都串起來了吧。
完結