前言
在上一節(jié)中我們看到SpringBoot方便快捷的POM依賴,這一節(jié)我們主要來看下SpringApplication.run的執(zhí)行流程。
public static void main(String[] args) throws Exception {
SpringApplication.run(StartSpringBootMain.class, args);
}
雖然只有一行代碼,不過這一行代碼卻做了大量的工作。這行代碼可以分成兩個(gè)部分:1、SpringApplication對象的創(chuàng)建;2、Spring容器的創(chuàng)建(即run方法的執(zhí)行)
說明:本節(jié)源碼的分析基于spring-boot-1.5.4.RELEASE。
SpringApplication對象的創(chuàng)建
在SpringApplication.run()的調(diào)用執(zhí)行過程中會創(chuàng)建出SpringApplication對象,然后委托給該對象的run方法。
public SpringApplication(Object... sources) {
/*
*sources="cn.zgc.springboot.basic.StartSpringBootMain",
*同時(shí)可以發(fā)現(xiàn)SpringApplication.run方法可以接收多個(gè)啟動配置類。
*/
initialize(sources);
}
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判斷當(dāng)前應(yīng)用是否為web應(yīng)用
this.webEnvironment = deduceWebEnvironment();
// 加載所有可用的ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 加載所有可用的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 判斷main方法的啟動類
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication類在實(shí)例化的過程中做了以下事情:
1、判斷當(dāng)前應(yīng)用類型是web還是標(biāo)準(zhǔn)的Standalone,這是因?yàn)樗鼈儗?yīng)的ApplicaitonContext類不同。通過deduceWebEnvironment方法完成,在該方法中會檢測classpath中是否同時(shí)存在類"javax.servlet.Servlet"和 "org.springframework.web.context.ConfigurableWebApplicationContext";
2、加載Classpath中所有的ApplicationContextInitializer類;
3、加載classpath中所有的ApplicationListener類;
4、推斷并設(shè)置main方法的定義類。
deduceWebEnvironment()的源碼如下
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
ApplicationContextInitializer的作用
ApplicationContextInitializer接口是在spring容器刷新之前執(zhí)行的一個(gè)回調(diào)函數(shù),在Spring容器創(chuàng)建之前會先調(diào)用ApplicationContextInitializer類中的initialize方法

那默認(rèn)加載的這些
ApplicationContextInitializer類名是從哪里獲取的呢?通過SpringFactoriesLoader.loadFactoryNames(type, classLoader)讀取spring-boot-1.5.4.RELEASE.jar/META-INF/spring.factories和spring-boot-autoconfigure.1.5.4.RELEASE.jar/META-INF/spring.factories文件中的org.springframework.context.ApplicationContextInitializer定義的值。
ApplicationListener的作用
ApplicationListener用來在Spring容器初始化完成之后,進(jìn)行一些常用的操作,例如初始化緩存、特定任務(wù)的注冊等。在SpringApplication類的初始化的過程中,會默認(rèn)加載10個(gè)ApplicationListener類。這些類和ApplicationContextInitializer一樣定義在spring.factories文件中。

創(chuàng)建容器
實(shí)例化完SpringApplication之后,接著會調(diào)用run方法。run方法執(zhí)行完之后,Spring容器也創(chuàng)建好了,先來看看run的源碼。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
// 通過SpringFactoriesLoader查找并加載所有的SpringApplicationRunListener
// SpringApplicationRunListener 可以監(jiān)聽springboot應(yīng)用啟動過程中的一些生命周期事件,并做一些處理
SpringApplicationRunListeners listeners = getRunListeners(args);
// 調(diào)用SpringFactoriesLoader的starting方法,廣播SpringBoot要開始執(zhí)行了。
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍歷調(diào)用所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準(zhǔn)備完畢。
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 打印Banner,Banner圖案支持自定義
Banner printedBanner = printBanner(environment);
//根據(jù)webEnvironment的值來決定創(chuàng)建何種類型的ApplicationContext對象
//如果是web環(huán)境,則創(chuàng)建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
//否則創(chuàng)建org.springframework.context.annotation.AnnotationConfigApplicationContext
context = createApplicationContext();
// 異常分析器
analyzers = new FailureAnalyzers(context);
//為ApplicationContext加載environment,之后逐個(gè)執(zhí)行ApplicationContextInitializer的initialize()方法來進(jìn)一步封裝ApplicationContext,
//并調(diào)用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個(gè)空的contextPrepared()方法】,
//之后初始化IoC容器,并調(diào)用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成,
//這里就包括通過@EnableAutoConfiguration導(dǎo)入的各種自動配置類。
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//初始化所有通過@EnableAutoConfiguration導(dǎo)入的各種自動配置類,調(diào)用ApplicationContext的refresh()方法
refreshContext(context);
//遍歷所有注冊的ApplicationRunner和CommandLineRunner,并執(zhí)行其run()方法。
//該過程可以理解為是SpringBoot完成ApplicationContext初始化前的最后一步工作,
//我們可以實(shí)現(xiàn)自己的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啟動過程進(jìn)行擴(kuò)展。
afterRefresh(context, applicationArguments);
// 調(diào)用所有的SpringApplicationRunListener的finished()方法,廣播SpringBoot已經(jīng)完成了ApplicationContext初始化的全部過程。
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
SpringApplication.run方法的執(zhí)行就是創(chuàng)建Spring容器的過程。run方法執(zhí)行過程中將SpringBoot的生命周期和監(jiān)聽器類SpringApplicationRunListener類綁定在一起,運(yùn)行過程中通過SpringApplicationRunListener廣播對象的事件。
public interface SpringApplicationRunListener {
//剛執(zhí)行run方法時(shí)
void started();
//環(huán)境建立好時(shí)候
void environmentPrepared(ConfigurableEnvironment environment);
//上下文建立好的時(shí)候
void contextPrepared(ConfigurableApplicationContext context);
//上下文載入配置時(shí)候
void contextLoaded(ConfigurableApplicationContext context);
//上下文刷新完成后,run方法執(zhí)行完之前
void finished(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener在SpringBoot中只有一個(gè)實(shí)現(xiàn)類org.springframework.boot.context.event.EventPublishingRunListener。它用于在SpringBoot啟動的時(shí)發(fā)布不同的事件類型(ApplicationEvent),如果有哪些ApplicationListener對這些事件敢興趣,則可以接收該事件并進(jìn)行處理。
自動配置類的加載注冊
在上一節(jié)中,我們看到SpringBoot會為我們加載很多自動配置類。而自動配置類是在SpringApplication.run的執(zhí)行過程中加載并注冊到Spring容器中的,更具體點(diǎn)是在SpringApplication.refreshContext()方法中執(zhí)行的。具體方法調(diào)用棧如下:

可以看到refreshContext的執(zhí)行過程中會涉及到Bean后置處理器
ConfigurationClassPostProcessor(后置處理器是Spring中很重要的一種機(jī)制,后面單獨(dú)寫篇文章介紹一下)。而ConfigurationClassPostProcessor又會去調(diào)用ConfigurationClassParser對自動配置類進(jìn)行解析。ConfigurationClassParser是個(gè)局部變量,它定義在ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法內(nèi)。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
...
}
由注釋可以知道ConfigurationClassParser類的作用是解析帶有@Configuration注解的類。
擴(kuò)展閱讀