自動(dòng)配置核心類(lèi)SpringFactoriesLoader
上面在說(shuō)@EnableAutoConfiguration的時(shí)候有說(shuō)META-INF下的spring.factories文件,那么這個(gè)文件是怎么被spring加載到的呢,其實(shí)就是SpringFactoriesLoader類(lèi)藕届。
SpringFactoriesLoader是一個(gè)供Spring內(nèi)部使用的通用工廠裝載器,SpringFactoriesLoader里有兩個(gè)方法况既,
// 加載工廠類(lèi)并實(shí)例化
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {}
// 加載工廠類(lèi)的類(lèi)名
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {}
在這個(gè)SpringBoot應(yīng)用啟動(dòng)過(guò)程中,SpringFactoriesLoader做了以下幾件事:
加載所有META-INF/spring.factories中的Initializer
加載所有META-INF/spring.factories中的Listener
加載EnvironmentPostProcessor(允許在Spring應(yīng)用構(gòu)建之前定制環(huán)境配置)
接下來(lái)加載Properties和YAML的PropertySourceLoader(針對(duì)SpringBoot的兩種配置文件的加載器)
各種異常情況的FailureAnalyzer(異常解釋器)
加載SpringBoot內(nèi)部實(shí)現(xiàn)的各種AutoConfiguration
模板引擎TemplateAvailabilityProvider(如Freemarker、Thymeleaf接奈、Jsp咐柜、Velocity等)
總得來(lái)說(shuō)兼蜈,SpringFactoriesLoader和@EnableAutoConfiguration配合起來(lái),整體功能就是查找spring.factories文件拙友,加載自動(dòng)配置類(lèi)为狸。
整體啟動(dòng)流程
在我們執(zhí)行入口類(lèi)的main方法之后,運(yùn)行SpringApplication.run遗契,后面new了一個(gè)SpringApplication對(duì)象辐棒,然后執(zhí)行它的run方法。
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
初始化SpringApplication類(lèi)
創(chuàng)建一個(gè)SpringApplication對(duì)象時(shí),會(huì)調(diào)用它自己的initialize方法
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 根據(jù)標(biāo)志類(lèi)javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext是否存在漾根,判斷是否是web環(huán)境
this.webEnvironment = deduceWebEnvironment();
// 通過(guò)SpringFactoriesLoader泰涂,獲取到所有META-INF/spring.factories中的ApplicationContextInitializer,并實(shí)例化
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 通過(guò)SpringFactoriesLoader立叛,獲取到所有META-INF/spring.factories中的ApplicationListener负敏,并實(shí)例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 獲取執(zhí)行當(dāng)前main方法的類(lèi),也就是啟動(dòng)類(lèi)
this.mainApplicationClass = deduceMainApplicationClass();
}
執(zhí)行核心run方法
初始化initialize方法執(zhí)行完之后秘蛇,會(huì)調(diào)用run方法其做,開(kāi)始啟動(dòng)SpringBoot。
public ConfigurableApplicationContext run(String... args) {
// 啟動(dòng)任務(wù)執(zhí)行的時(shí)間監(jiān)聽(tīng)器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
// 設(shè)置系統(tǒng)java.awt.headless屬性赁还,確定是否開(kāi)啟headless模式(默認(rèn)開(kāi)啟headless模式)
configureHeadlessProperty();
// 通過(guò)SpringFactoriesLoader妖泄,獲取到所有META-INF/spring.factories下的SpringApplicationRunListeners并實(shí)例化
SpringApplicationRunListeners listeners = getRunListeners(args);
// 開(kāi)始廣播啟動(dòng)
listeners.started();
try {
// 創(chuàng)建SpringBoot默認(rèn)啟動(dòng)參數(shù)對(duì)象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根據(jù)啟動(dòng)參數(shù)創(chuàng)建并配置Environment(所有有效的配置,如Profile)艘策,并遍歷所有的listeners蹈胡,廣播啟動(dòng)環(huán)境已準(zhǔn)備
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
// 打印啟動(dòng)圖案
Banner printedBanner = printBanner(environment);
// 根據(jù)標(biāo)志類(lèi)(上面有提到過(guò)),創(chuàng)建對(duì)應(yīng)類(lèi)型的ApplicationContext
context = createApplicationContext();
// 創(chuàng)建異常解析器(當(dāng)啟動(dòng)失敗時(shí)朋蔫,由此解析器處理失敗結(jié)果)
analyzers = new FailureAnalyzers(context);
// 準(zhǔn)備Spring上下文環(huán)境
// 在這個(gè)方法中罚渐,主要完成了以下幾件事:
// 1、設(shè)置SpringBoot的環(huán)境配置(Environment)
// 2驯妄、注冊(cè)Spring Bean名稱(chēng)的序列化器BeanNameGenerator荷并,并設(shè)置資源加載器ResourceLoader
// 3、加載ApplicationContextInitializer初始化器青扔,并進(jìn)行初始化
// 4源织、統(tǒng)一將上面的Environment、BeanNameGenerator微猖、ResourceLoader使用默認(rèn)的Bean注冊(cè)器進(jìn)行注冊(cè)
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
// 注冊(cè)一個(gè)關(guān)閉Spring容器的鉤子
refreshContext(context);
// 獲取當(dāng)前所有ApplicationRunner和CommandLineRunner接口的實(shí)現(xiàn)類(lèi)谈息,執(zhí)行其run方法
// ApplicationRunner和CommandLineRunner功能基本一樣,在Spring容器啟動(dòng)完成時(shí)執(zhí)行凛剥,唯一不同的是ApplicationRunner的run方法入?yún)⑹茿pplicationArguments侠仇,而CommandLineRunner是String數(shù)組
afterRefresh(context, applicationArguments);
// 通知所有l(wèi)istener,Spring容器啟動(dòng)完成
listeners.finished(context, null);
// 停止時(shí)間監(jiān)聽(tīng)器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable ex) {
// 啟動(dòng)有異常時(shí)傅瞻,調(diào)用異常解析器解析異常信息,根據(jù)異常級(jí)別盲憎,判斷是否退出Spring容器
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
首先遍歷執(zhí)行所有通過(guò)SpringFactoriesLoader嗅骄,在當(dāng)前classpath下的META-INF/spring.factories中查找所有可用的SpringApplicationRunListeners并實(shí)例化。調(diào)用它們的starting()方法饼疙,通知這些監(jiān)聽(tīng)器SpringBoot應(yīng)用啟動(dòng)溺森。
創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment慕爬,包括當(dāng)前有效的PropertySource以及Profile。
遍歷調(diào)用所有的SpringApplicationRunListeners的environmentPrepared()的方法屏积,通知這些監(jiān)聽(tīng)器SpringBoot應(yīng)用的Environment已經(jīng)完成初始化医窿。
打印SpringBoot應(yīng)用的banner,SpringApplication的showBanner屬性為true時(shí)炊林,如果classpath下存在banner.txt文件姥卢,則打印其內(nèi)容,否則打印默認(rèn)banner渣聚。
根據(jù)啟動(dòng)時(shí)設(shè)置的applicationContextClass和在initialize方法設(shè)置的webEnvironment独榴,創(chuàng)建對(duì)應(yīng)的applicationContext。
創(chuàng)建異常解析器奕枝,用在啟動(dòng)中發(fā)生異常的時(shí)候進(jìn)行異常處理(包括記錄日志棺榔、釋放資源等)。
設(shè)置SpringBoot的Environment隘道,注冊(cè)Spring Bean名稱(chēng)的序列化器BeanNameGenerator症歇,并設(shè)置資源加載器ResourceLoader,通過(guò)SpringFactoriesLoader加載ApplicationContextInitializer初始化器谭梗,調(diào)用initialize方法忘晤,對(duì)創(chuàng)建的ApplicationContext進(jìn)一步初始化。
調(diào)用所有的SpringApplicationRunListeners的contextPrepared方法激捏,通知這些Listener當(dāng)前ApplicationContext已經(jīng)創(chuàng)建完畢设塔。
最核心的一步,將之前通過(guò)@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經(jīng)準(zhǔn)備完畢的ApplicationContext缩幸。
調(diào)用所有的SpringApplicationRunListener的contextLoaded方法壹置,加載準(zhǔn)備完畢的ApplicationContext竞思。
調(diào)用refreshContext表谊,注冊(cè)一個(gè)關(guān)閉Spring容器的鉤子ShutdownHook,當(dāng)程序在停止的時(shí)候釋放資源(包括:銷(xiāo)毀Bean盖喷,關(guān)閉SpringBean的創(chuàng)建工廠等)
注: 鉤子可以在以下幾種場(chǎng)景中被調(diào)用:
1)程序正常退出
2)使用System.exit()
3)終端使用Ctrl+C觸發(fā)的中斷
4)系統(tǒng)關(guān)閉
5)使用Kill pid命令殺死進(jìn)程
獲取當(dāng)前所有ApplicationRunner和CommandLineRunner接口的實(shí)現(xiàn)類(lèi)爆办,執(zhí)行其run方法
遍歷所有的SpringApplicationRunListener的finished()方法,完成SpringBoot的啟動(dòng)课梳。