文接 深入學(xué)習(xí) Spring Boot:Spring Boot啟動(dòng)分析(上)
2础嫡、啟動(dòng) Spring 應(yīng)用程序run()
Spring Boot首先幫我們實(shí)例化了一個(gè)SpringApplication對(duì)象屡立,接下類(lèi)調(diào)用這個(gè)對(duì)象的run方法,創(chuàng)建和刷新一個(gè)新的ApplicationContext
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
2.1 啟動(dòng)一個(gè)StopWatch測(cè)量程序的運(yùn)行時(shí)間柳击。StopWatch可以在開(kāi)發(fā)和調(diào)試階段驗(yàn)證程序的性能
2.2 使用Headless模式:
System.setProperty("java.awt.headless",System.getProperty("java.awt.headless", true);
Headless模式是系統(tǒng)的一種配置模式。在該模式下坤候,系統(tǒng)缺少了顯示設(shè)備喜每、鍵盤(pán)或鼠標(biāo)。Spring Boot程序一般是服務(wù)端程序是己,服務(wù)器往往缺少前述硬件又兵,但又需要使用他們提供的功能,生成相應(yīng)的數(shù)據(jù)卒废,以提供給客戶端(比如在console生成spring神獸沛厨,繪制驗(yàn)證碼之類(lèi)的)。此時(shí)升熊,我們可以在程序開(kāi)始激活headless模式俄烁,告訴程序,現(xiàn)在你要工作在Headless mode下级野,就不要指望硬件幫忙了页屠,你得自力更生,依靠系統(tǒng)的計(jì)算能力模擬出這些特性來(lái)蓖柔。
2.3 通過(guò)SpringFactoriesLoader加載SpringApplicationRunListener
并啟動(dòng)辰企,用以監(jiān)聽(tīng)SpringApplication的run方法產(chǎn)生的各類(lèi)事件。SpringApplicationRunListeners是對(duì)SpringApplicationRunListener實(shí)例集合的一個(gè)封裝况鸣。在這里牢贸,SpringApplication只加載了一個(gè)Listener:EventPublishingRunListener
,事實(shí)上這個(gè)類(lèi)是充當(dāng)著事件廣播器的作用镐捧,它可以將run方法中的事件(如starting潜索、environmentPrepared)封裝為Event對(duì)象,發(fā)布到我們之前加載的ApplicationListener中懂酱。2.4 創(chuàng)建和配置ConfigurableEnvironment:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
a竹习、根據(jù)我們之前檢測(cè)出來(lái)的webApplicationType
創(chuàng)建一個(gè)ConfigurableEnvironment對(duì)象(若this.webApplicationType == WebApplicationType.SERVLET
則創(chuàng)建其子類(lèi)StandardServletEnvironment()對(duì)象,否則創(chuàng)建StandardEnvironment對(duì)象)列牺。在實(shí)例化一個(gè)ConfigurableEnvironment對(duì)象時(shí)整陌,程序會(huì)讀取運(yùn)行環(huán)境的各種數(shù)據(jù),如"java.vm.version" -> "25.73-b02"瞎领;
b泌辫、將我們?cè)趩?dòng)應(yīng)用程序時(shí)帶入的參數(shù)(如“--debug”),配置到ConfigurableEnvironment對(duì)象中九默;
c震放、調(diào)用listeners.environmentPrepared(environment)
,發(fā)布事件荤西。在這里澜搅,EventPublishingRunListener
會(huì)封裝一個(gè)ApplicationEnvironmentPreparedEvent
伍俘,然后發(fā)布到各個(gè)Listener中。Listener執(zhí)行的動(dòng)作我們暫時(shí)不分析勉躺,明顯可以看到的就是在debug模式下癌瘾,console會(huì)打印第一行日志,顯示應(yīng)用程序的運(yùn)行環(huán)境饵溅;
d妨退、將environment綁定到SpringApplication上
e、在environment.propertySources中添加(如果沒(méi)有的話)一個(gè)ConfigurationPropertySourcesPropertySource
對(duì)象蜕企,使得environment管理的PropertySource對(duì)象能適配 PropertySourcesPropertyResolver咬荷,能夠通過(guò)屬性名get到具體的配置,詳細(xì)見(jiàn) ConfigurationPropertySources
2.5 將系統(tǒng)屬性“spring.beaninfo.ignore”設(shè)置為true轻掩,跳過(guò)掃描BeanInfo類(lèi)幸乒,防止重復(fù)加載bean。詳見(jiàn)IGNORE_BEANINFO_PROPERTY_NAME
2.6 調(diào)用
printBanner
打印“Spring神獸”2.7 根據(jù)webApplicationType創(chuàng)建一個(gè)ConfigurableApplicationContext對(duì)象唇牧。在本例中罕扎,webApplicationType為”SERVLET“,創(chuàng)建的對(duì)象為"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
2.8 加載spring.factories中的SpringBootExceptionReporter實(shí)現(xiàn)類(lèi)
2.9 準(zhǔn)備Context:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
a丐重、setEnvironment(environment)
b腔召、postProcessApplicationContext(context)
,做一些善后工作:如果成員變量beanNameGenerator不為Null扮惦,那么為ApplicationContext對(duì)象注冊(cè)beanNameGenerator bean臀蛛;如果成員變量resourceLoader不為null,則為ApplicationContext對(duì)象設(shè)置ResourceLoader崖蜜。
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
c浊仆、依次調(diào)用SpringApplication的initializers中的初始化器:
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
在本例中,initializers列表及其任務(wù)為:
initializers = {ArrayList@952} size = 6
// 讀取key為”context.initializer.classes“的配置豫领,實(shí)例化Initializer并執(zhí)行initialize();
0 = {DelegatingApplicationContextInitializer@1057}
// 設(shè)置context的id氧卧,本例等于”application“;
1 = {ContextIdApplicationContextInitializer@1058}
// 為context.beanFactoryPostProcessor增加一個(gè)ConfigurationWarningsPostProcessor對(duì)象氏堤,報(bào)告warning等級(jí)的錯(cuò)誤配置
2 = {ConfigurationWarningsApplicationContextInitializer@1059}
// 為context添加一個(gè)ApplicationListener,監(jiān)聽(tīng)WebServerInitializedEvent
3 = {ServerPortInfoApplicationContextInitializer@1060}
// 為context.beanFactoryPostProcessor增加一個(gè)CachingMetadataReaderFactoryPostProcessor
4 = {SharedMetadataReaderFactoryContextInitializer@1061}
//為context添加一個(gè)AutoConfigurationReportListener搏明,用以監(jiān)聽(tīng)自動(dòng)配置報(bào)告
5 = {AutoConfigurationReportLoggingInitializer@1062}
d鼠锈、調(diào)用監(jiān)聽(tīng)器,報(bào)告contextPrepared事件星著;
e购笆、打印啟動(dòng)日志:
2017-09-10 20:18:51.937 INFO 28314 --- [ main] com.zhuangqf.learn.App : Starting App on zhuangqinfa with PID 28314 (/home/zhuangqf/workspace/spring/SpringBootDemo/target/classes started by zhuangqf in /home/zhuangqf/workspace/spring/SpringBootDemo)
2017-09-10 20:18:51.939 INFO 28314 --- [ main] com.zhuangqf.learn.App : No active profile set, falling back to default profiles: default
f、注冊(cè)指定的bean:"springApplicationArguments"和"springBootBanner":
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
簡(jiǎn)單而言虚循, context的beanFactory是一個(gè)DefaultListableBeanFactory對(duì)象同欠,其內(nèi)部有多個(gè)Map<String, Object> 用來(lái)存放注冊(cè)的bean样傍。
g、加載context.primarySources铺遂,在本例中為App.class衫哥,其主要是加載App上的注解或默認(rèn)的配置文件。
- 2.10 refreshContext(context)
加載或刷新持久化形式的配置(如xml文件襟锐、properties文件撤逢,和數(shù)據(jù)庫(kù)信息)。
由于這是一個(gè)啟動(dòng)方法, 如果它失敗了, 它應(yīng)該銷(xiāo)毀已經(jīng)創(chuàng)建的單例, 以避免懸空的資源粮坞。換言之, 在調(diào)用該方法之后, 所有的單例bean都不應(yīng)實(shí)例化蚊荣。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
整個(gè)方法比較復(fù)雜,我們一點(diǎn)點(diǎn)解析:
a莫杈、準(zhǔn)備刷新prepareRefresh()
互例,完成了以下任務(wù):
設(shè)置context的startupDate為當(dāng)前時(shí)間;
設(shè)置closed標(biāo)志位為false筝闹,active標(biāo)志為true媳叨;
讀取Property配置到environment中;
檢查environment必設(shè)的配置是否為null丁存;
初始化this.earlyApplicationEvents
作為存放發(fā)布時(shí)間的Set
b肩杈、obtainFreshBeanFactory()
:銷(xiāo)毀原有的beanFactory類(lèi),并新建一個(gè)beanFactory返回
c解寝、prepareBeanFactory(beanFactory)
:設(shè)置beanFactory的各個(gè)屬性扩然,由AbstractApplicationContext實(shí)現(xiàn);
d聋伦、postProcessBeanFactory(beanFactory)
:對(duì)beanFactory根據(jù)具體的Context子類(lèi)設(shè)置不同的屬性夫偶,例如,本例中context的具體類(lèi)型為ServletWebServerApplicationContext觉增,會(huì)在beanFactory中注冊(cè)一個(gè)ServletContextAwareProcessor兵拢。
e、invokeBeanFactoryPostProcessors(beanFactory);
調(diào)用注冊(cè)到beanFactory中的postRrocessors逾礁。