Spring Boot 源碼分析-啟動(dòng)流程(1)

源碼版本

  • spring boot 2.1.0.RELEASE


入口代碼


@SpringBootApplication

public class Run {

public static void main(String[] args) {

SpringApplication app = new SpringApplication(Run.class); // 1

app.run(args);  // 2

}

}

構(gòu)造方法中的加載過(guò)程


在new SpringApplication(Run.class)中實(shí)際調(diào)用的是SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)構(gòu)造方法,在構(gòu)造方法中主要執(zhí)行了一些必要的查找初始化工作。





public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

this.resourceLoader = resourceLoader;

Assert.notNull(primarySources, "PrimarySources must not be null");

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 

this.webApplicationType = WebApplicationType.deduceFromClasspath();  // 1-1

setInitializers((Collection) getSpringFactoriesInstances(

ApplicationContextInitializer.class));                      // 1-2

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 1-3

this.mainApplicationClass = deduceMainApplicationClass();

}


1-1. 主要通過(guò)classpath中的類判斷應(yīng)用什么類型(WebApplicationType NONE, SERVLET, REACTIVE)

1-2. 掃描classpath:META-INF/spring.factories 文件中定義的 ApplicationContextInitializer 實(shí)現(xiàn)類,并創(chuàng)建實(shí)例

1-3. 掃描classpath:META-INF/spring.factories 文件中定義的 ApplicationListener 實(shí)現(xiàn)類,并創(chuàng)建實(shí)例

spring.factories加載的 ApplicationContextInitializer,ApplicationListener 在全局啟動(dòng)過(guò)程中具有非常高的運(yùn)行優(yōu)先級(jí),并且ApplicationListener 會(huì)在 ApplicationContextInitializer前被觸發(fā)調(diào)用,配置文件的加載解析也是在 ApplicationListener 中完成,后面會(huì)進(jìn)行補(bǔ)充說(shuō)明



run方法啟動(dòng)過(guò)程



public ConfigurableApplicationContext run(String... args) {

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

configureHeadlessProperty();

SpringApplicationRunListeners listeners = getRunListeners(args);  // 2-1

listeners.starting();

try {

ApplicationArguments applicationArguments = new DefaultApplicationArguments(

args);

ConfigurableEnvironment environment = prepareEnvironment(listeners,

applicationArguments);    // 2-2

configureIgnoreBeanInfo(environment); // 2-3

Banner printedBanner = printBanner(environment);

context = createApplicationContext();  // 2-4

exceptionReporters = getSpringFactoriesInstances(

SpringBootExceptionReporter.class,

new Class[] { ConfigurableApplicationContext.class }, context);  // 2-5

prepareContext(context, environment, listeners, applicationArguments,

printedBanner);    // 2-6

refreshContext(context);    // 2-7

afterRefresh(context, applicationArguments);    // 2-8

stopWatch.stop();

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass)

.logStarted(getApplicationLog(), stopWatch);

}

listeners.started(context);  // 2-9

callRunners(context, applicationArguments);  // 2-10

}

catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, listeners);

throw new IllegalStateException(ex);

}

try {

listeners.running(context);    // 2-11

}

catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, null);

throw new IllegalStateException(ex);

}

return context;

}


2-1. 掃描classpath:META-INF/spring.factories 文件中定義的 SpringApplicationRunListener 實(shí)現(xiàn)類,并創(chuàng)建實(shí)例,然后將實(shí)例組合到 SpringApplicationRunListeners 實(shí)例中,普通web應(yīng)用默認(rèn)情況下其實(shí)只是加載了一個(gè) EventPublishingRunListener 實(shí)例,該實(shí)例在啟動(dòng)過(guò)程中會(huì)使用(SimpleApplicationEventMulticaster)來(lái)發(fā)送各類事件給監(jiān)聽(tīng)器

listeners.starting() 觸發(fā)了一個(gè) ApplicationStartingEvent 事件,這時(shí)就已經(jīng)可以觸發(fā)前面初始化加載的ApplicationListener監(jiān)聽(tīng)器了


2-2. 主要做了如下幾件事(只做簡(jiǎn)單描述,后面再做詳細(xì)解刨)

2-2-1. 根據(jù)前面判斷的程序類型 WebApplicationType 創(chuàng)建對(duì)應(yīng)的Environment實(shí)例

2-2-2. 初始化和設(shè)置 ConversionService (用于各種類型的轉(zhuǎn)換)

2-2-3. 配置 MutablePropertySources 將命令行參數(shù)加入到屬性最前面,如果有的話.

(StandardServletEnvironment 在創(chuàng)建實(shí)例時(shí)就默認(rèn)加載了 

  [StubPropertySource {name='servletConfigInitParams'}, 

StubPropertySource {name='servletContextInitParams'}, 

MapPropertySource {name='systemProperties'}, 

SystemEnvironmentPropertySource {name='systemEnvironment'}])

2-2-4. 通過(guò)上一步加載的PropertySources 來(lái)設(shè)置 ActiveProfiles

2-2-5. 通過(guò) SpringApplicationRunListeners 觸發(fā)ApplicationEnvironmentPreparedEvent 事件

(用于加載配置文件的監(jiān)聽(tīng)類 ConfigFileApplicationListener 便是監(jiān)聽(tīng)了該事件,yml或properties配置文件便是在這時(shí)加載到環(huán)境中)

2-2-6. SpringConfigurationPropertySources PropertySourcesPlaceholdersResolver 及2-2-3處的PropertySources綁定到一起用于后的屬性獲取包括將表達(dá)式解析成最終值 如 server.port=${random.int[8080,8090]}


2-3. configureIgnoreBeanInfo(environment) 在System.setProperty 中如果不存在 "spring.beaninfo.ignore" 則設(shè)置該屬性,默認(rèn)為true


2-4. createApplicationContext() 會(huì)根據(jù) WebApplicationType 來(lái)創(chuàng)建 ConfigurableApplicationContext 的實(shí)例。 如 WebApplicationType 為 SERVLET 將創(chuàng)建 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 的實(shí)例。

此時(shí)需要注意在 AnnotationConfigServletWebServerApplicationContext 被創(chuàng)建時(shí) 屬性中 environment 將被創(chuàng)建一個(gè)新的 Environment 實(shí)例,該實(shí)例這時(shí)并非為 2-2 中所創(chuàng)建的 Environment。AnnotationConfigServletWebServerApplicationContext 中 初始化了兩個(gè)關(guān)鍵類 [AnnotatedBeanDefinitionReader, ClassPathBeanDefinitionScanner]

在創(chuàng)建 AnnotatedBeanDefinitionReader 時(shí)通過(guò)AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) 將如下類交由beanFactory托管


org.springframework.context.annotation.internalConfigurationAnnotationProcessor: org.springframework.context.annotation.ConfigurationClassPostProcessor 

org.springframework.context.annotation.internalAutowiredAnnotationProcessor: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

org.springframework.context.annotation.internalCommonAnnotationProcessor: org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

org.springframework.context.annotation.internalPersistenceAnnotationProcessor: org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor

org.springframework.context.event.internalEventListenerProcessor: org.springframework.context.event.EventListenerMethodProcessor

org.springframework.context.event.internalEventListenerFactory: org.springframework.context.event.DefaultEventListenerFactory

創(chuàng)建 ClassPathBeanDefinitionScanner 時(shí)初始化了 AnnotationTypeFilter (用戶匹配元注解)


2-5. 這里比較簡(jiǎn)單,主要是掃描classpath:META-INF/spring.factories 文件中定義的 SpringBootExceptionReporter 實(shí)現(xiàn)類并初始化,這些類用于處理啟動(dòng)過(guò)程中的錯(cuò)誤


2-6. 這一步主要包含如下幾個(gè)重要步驟

2-6-1. 將在 2-4 中所創(chuàng)建的 AnnotationConfigServletWebServerApplicationContext 的 environment屬性替換為 2-2-1 中所創(chuàng)建的 Environment 實(shí)例

2-6-2. 這里主要是將前面 2-2-2 中創(chuàng)建的 ConversionService 設(shè)置到 context.beanFactory.conversionService 中

2-6-3. 前面1-2中通過(guò)掃描classpath:META-INF/spring.factories ApplicationContextInitializer將在這一步被調(diào)用

2-6-4. 觸發(fā) ApplicationContextInitializedEvent 事件(此時(shí)因還未refresh context加載bean 所以該事件還是只有 spring.factories 文件中掃描加載的的ApplicationListener能監(jiān)聽(tīng)到)

2-6-5. 將啟動(dòng)命令的args封裝成的 ApplicationArguments 注入到context 的 beanFactory 中,并設(shè)置 beanFactory 的一些屬性配置

2-6-6. 通過(guò)啟動(dòng)入口sources(1處傳入的class)作為參數(shù)創(chuàng)建了 BeanDefinitionLoader 。 BeanDefinitionLoader在創(chuàng)建過(guò)程中同樣會(huì)創(chuàng)建 [AnnotatedBeanDefinitionReader, ClassPathBeanDefinitionScanner] 這與 2-4 中的實(shí)例并不相同,但 2-4 已經(jīng)在AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) 注入了一些關(guān)鍵類,這里不會(huì)重復(fù)注入

創(chuàng)建ClassPathBeanDefinitionScanner添加了排除過(guò)濾器 ClassExcludeFilter(sources)

然后通過(guò)調(diào)用BeanDefinitionLoader.load() 方法 將入口Class加入到context beanFactory托管

2-6-7. 觸發(fā) ApplicationPreparedEvent 事件。此處觸發(fā)事件前做了一些額外的工作。

將循環(huán)判斷從 spring.factories 文件中掃描加載的的 ApplicationListener 或者 在創(chuàng)建 SpringApplication 手動(dòng)設(shè)置的 ApplicationListener (存儲(chǔ)在SpringApplication.listeners中),如果listener實(shí)現(xiàn)了 ApplicationContextAware 接口,將會(huì)把context設(shè)置到listener實(shí)例屬性中。

將listener加入到 context 的 applicationListeners 屬性中。


2-7. refreshContext(context)應(yīng)該是處理最復(fù)雜的一步了,各種托管bean的初始化,掃描,加載,裝配都在這一步完成,后面用專門(mén)文章針對(duì)這一方法做仔細(xì)說(shuō)明.


2-8. afterRefresh 為一個(gè)空方法,并未做任何實(shí)現(xiàn)


2-9. 觸發(fā) ApplicationStartedEvent 事件,這里也有些不同,前面都是通過(guò)EventPublishingRunListener.SimpleApplicationEventMulticaster 來(lái)觸發(fā),這里會(huì)使用context中的SimpleApplicationEventMulticaster來(lái)觸發(fā),這時(shí)因各種bean已經(jīng)在2-7已經(jīng)初始化完成,所以實(shí)現(xiàn)了相應(yīng)監(jiān)聽(tīng)接口的類都能得到觸發(fā)。


2-10. 啟動(dòng)完成后從context中調(diào)用 ApplicationRunner, CommandLineRunner 調(diào)用執(zhí)行


2-11. 通過(guò)調(diào)用 context 觸發(fā) ApplicationReadyEvent 事件



總結(jié)


到這里只是簡(jiǎn)單的介紹了下spring boot的啟動(dòng)過(guò)程,說(shuō)明了再啟動(dòng)前做了哪些事,我們能從這些事情中能做到哪些切入點(diǎn),可以從哪一步來(lái)定義自己的功能,或者出現(xiàn)問(wèn)題能判斷出大致出現(xiàn)在哪一步。 從上面我們可以看到我們能做的最早的切入點(diǎn)便是 ApplicationListener, ApplicationListener觸發(fā)執(zhí)行時(shí),應(yīng)用bean此時(shí)還并未進(jìn)入初始化狀態(tài)。所以我們可以用 ApplicationListener 來(lái)做很多的準(zhǔn)備工作,比如改寫(xiě)配置,實(shí)現(xiàn)自己的配置屬性加載類等。而且我們可以看出在2-7之前所觸發(fā)的事件只有在spring.factories中定義的或者在程序創(chuàng)建時(shí)加入的listener能夠監(jiān)聽(tīng)到(如 ApplicationStartingEvent, ApplicationPreparedEvent),只有在2-7完成后觸發(fā)的事件采用spring常用注解托管的監(jiān)聽(tīng)器才能監(jiān)聽(tīng)到...



待續(xù)...




?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容