源碼版本
- 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ù)...