Spring Boot啟動流程源碼分析
版本:2.1.1.RELEASE
使用main方法啟動Spring Boot應用:
public static void main(String[] args) {
SpringApplication.run(DingtalkApplication.class, args);
}
進入SpringApplication類的run方法最終實作位置:
public ConfigurableApplicationContext run(String... args) {
// 1. 啟動計時
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 2. 回調接口SpringBootExceptionReporter用于支援自定義spring應用程式啟動錯誤的報告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 3. 配置啟用Java headless模式
configureHeadlessProperty();
// 4. 擷取Spring應用run方法的監聽器集合并啟動所有的監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 5. 提供對用于運作SpringApplication的參數的通路
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 6. 建立和配置環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 7. 配置忽略BeanInfo類的加載
configureIgnoreBeanInfo(environment);
// 8. 列印Banner
Banner printedBanner = printBanner(environment);
// 9. 建立ApplicationContext
context = createApplicationContext();
// 10. 擷取異常報告執行個體清單
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 11. 準備應用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 12. 重新整理底層的ApplicationContext
refreshContext(context);
// 13. protected方法,應用上下文重新整理後,子類可實作此方法用于後續的操作
afterRefresh(context, applicationArguments);
// 14. 列印應用啟動資訊
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 15. 啟動實作了CommandLineRunner 和 ApplicationRunner 接口的類的run方法
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 16. 在run 方法結束之前立即調用,釋出事件,應用程式已準備好接受服務請求
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
run方法源碼上注釋了大概流程,接下來繼續深入重點流程的源碼。
流程4,擷取Spring應用run方法的監聽器集合并啟動所有的監聽器:
getRunListeners(String[] args) 方法源碼:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
傳回SpringApplicationRunListeners 執行個體,直接看構造方法的第二個參數的getSpringFactoriesInstances方法實作:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 1. 擷取類加載器
ClassLoader classLoader = getClassLoader();
// 2. 擷取指定類型的工廠實作類的完全限定類名集合
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 3. 根據傳入的完全限定類名集合建立對應工廠執行個體
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 4. 根據工廠執行個體上的@Order注解指定的順序排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
該方法第一個參數這裡傳入的是SpringApplicationRunListener 接口,會擷取該接口所在類加載器下的“META-INF/spring.factories”屬性檔案
設定的接口實作org.springframework.boot.context.event.EventPublishingRunListener,然後調用該類的構造方法執行個體化:
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
該類的主要作用是作為應用啟動過程的事件釋出監聽器,可以看到構造方法中執行個體化了一個簡單的應用事件多點傳播器SimpleApplicationEventMulticaster 并周遊添加應用啟動事件監聽器。
流程4最後調用listeners.starting() 啟動監聽器,實作源碼:
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
該方法多點傳播一個ApplicationStartingEvent執行個體(應用啟動事件),事件源是SpringApplication本身。
接下來就是解析事件類型并調用對應的事件監聽器了,感興趣的可以自己深入。深入之前需要對Spring事件機制有所了解,
推薦此文Spring事件機制。
流程6. 建立和配置環境:
prepareEnvironment(listeners, applicationArguments)方法實作:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 建立環境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 釋出環境準備事件
listeners.environmentPrepared(environment);
// 綁定環境到此應用
bindToSpringApplication(environment);
// 判斷是否需要轉換環境
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 附加ConfigurationPropertySource支援到指定環境
ConfigurationPropertySources.attach(environment);
return environment;
}
其中,getOrCreateEnvironment() 方法實作:
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
// 根據ClassPath存在的類推斷應用運作環境,以下都是web環境
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
流程9. 建立ApplicationContext:
createApplicationContext方法實作:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
// 1. 初始化并傳回org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext Class對象
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
// 忽略異常捕獲代碼
}
// 2. 調用AnnotationConfigServletWebServerApplicationContext對象的預設構造方法執行個體化
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
流程11. 準備應用上下文:
prepareContext方法實作:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 1. 設定環境
context.setEnvironment(environment);
// 2. 在ApplicationContext中應用任何相關的後置處理,這裡為context對象的BeanFactory執行個體DefaultListableBeanFactory添加轉換服務
postProcessApplicationContext(context);
// 3. 在context重新整理之前應用實作了ApplicationContextInitializer回調接口的執行個體進行context上下文對象的初始化
applyInitializers(context);
// 4. 釋出context初始化事件
listeners.contextPrepared(context);
// 5. 列印應用版本資訊和激活的配置檔案資訊active profile
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 6. 添加名稱為springApplicationArguments,springBootBanner的單例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 7. 加載源(即main方法所在的類對象)不可變的集合對象并注冊其bean到應用上下文
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 8. 釋出應用準備事件
listeners.contextLoaded(context);
}
應用上下文對象準備好了,接下來就進行重新整理上下文操作。
12. 重新整理底層的ApplicationContext:
refreshContext方法實作:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
// 向JVM運作時注冊一個關機鈎子,在JVM關閉時同時關閉這個上下文。
context.registerShutdownHook();
}
......
}
}
進入到ServletWebServerApplicationContext類的refresh(context)方法實作:
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
......
}
發現是直接調用的父類AbstractApplicationContext的refresh方法實作:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 準備好重新整理上下文
prepareRefresh();
// 2. 告訴子類重新整理内部bean工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 準備bean工廠以用于此上下文中
prepareBeanFactory(beanFactory);
try {
// 4. 允許在特定的ApplicationContext實作中注冊特殊的bean後置處理器
postProcessBeanFactory(beanFactory);
// 5. 執行個體化并調用所有已注冊的BeanFactoryPostProcessor bean
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 執行個體化并調用所有已注冊的BeanPostProcessor bean
registerBeanPostProcessors(beanFactory);
// 7. 初始化MessageSource用于目前上下文,提供參數化和i18n的支援
initMessageSource();
// 8. 初始化事件多路廣播用于目前上下文,預設使用SimpleApplicationEventMulticaster單例bean
initApplicationEventMulticaster();
// 9. 在特定上下文子類中初始化其他特殊bean。
onRefresh();
// 10. 檢查監聽器bean并注冊它們
registerListeners();
// 11. 執行個體化剩餘所有非懶加載的單例bean
finishBeanFactoryInitialization(beanFactory);
// 12. 最後一步: 釋出相應的事件
finishRefresh();
}
catch (BeansException ex) {
......
// 銷毀所有建立的單例來避免懸空資源
destroyBeans();
// 重置 'active' 辨別.
cancelRefresh(ex);
// 抛出異常給調用者
throw ex;
}
finally {
// 重置Spring的公共反射中繼資料緩存
resetCommonCaches();
}
}
}
第一點主要做了以下操作:
- 清除本地中繼資料緩存(如果有的話),删除所有緩存的類中繼資料。
- 設定其啟動日期和活動标志以及執行任何屬性源的初始化。
第二點主要做了以下操作:
- 将成員變量 refreshed 設為 true。
- 為 DefaultListableBeanFactory 指定一個用于序列化的id。
第三點主要做了以下操作:
- 配置工廠的标準上下文特征,例如上下文的類加載器和後置處理程式。
- 所有bean定義都已加載,但還沒有執行個體化bean。
第四點主要做了以下操作:
- 注冊特定應用上下文的後置處理器bean
- 掃描basePackage指定的包路徑
- 注冊被注解的類,例如@Configuration
第五點主要做了以下操作:
- 執行個體化并調用所有已注冊的BeanFactoryPostProcessor bean,如果給定顯式順序,則遵循顯式順序
- 分别調用實作了BeanFactoryPostProcessor接口的bean。
第六點主要做了以下操作:
- 執行個體化并調用所有已注冊的BeanPostProcessor bean,如果給定顯式順序,則遵循顯式順序
- 分别調用實作了BeanPostProcessor接口的bean。
第九點主要做了以下操作:
- 預設建立TomcatWebServer
- 初始化WebApplicationContext和SerlvetContext參數
第十一點完成BeanFactory的初始化并執行個體化剩餘的單例bean:
finishBeanFactoryInitialization方法實作:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化用于此上下文的轉換服務
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 如果沒有内嵌value解析器Bean則注冊一個(例如 PropertyPlaceholderConfigurer bean),主要用于解析${}占位符.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 盡早地初始化LoadTimeWeaverAware bean以允許盡早地注冊其變換器
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 停止正用于類型比對的臨時類加載器
beanFactory.setTempClassLoader(null);
// 緩存所有bean定義的中繼資料,不接受後面的改變
beanFactory.freezeConfiguration();
// 執行個體化所有剩餘的(非懶加載)單例bean
beanFactory.preInstantiateSingletons();
}
其中重點看最後一步preInstantiateSingletons方法的實作:
@Override
public void preInstantiateSingletons() throws BeansException {
......
// 疊代一個beanDefinitionNames的副本以允許init方法,這些方法又輪流注冊新的bean定義。
// 雖然這可能不是正常工廠引導程式的一部分,但它确實可以正常工作。
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 觸發所有非懶加載單例bean的初始化...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 如果不是抽象的bean并且是非懶加載的單例bean,則進行
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判斷是否是FactoryBean,如果是則進一步判斷是否需要盡早的初始化bean,否則直接初始化bean
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// 觸發所有可用單例bean的afterSingletonsInstantiated方法回調...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
接下來看getBean(beanName)方法的底層實作,是直接調用doGetBean方法,傳回指定bean的執行個體,該執行個體可以是共享的或獨立的:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 1. 傳回bean名稱,必要時删除工廠字首,并将别名解析為規範名稱。
final String beanName = transformedBeanName(name);
Object bean;
// 2. 急切地檢查單例緩存以手動地注冊單例
Object sharedInstance = getSingleton(beanName);
// 共享的單例bean執行個體不為空并且args為空
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
// 3. 判斷該目前bean是否在建立中
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 4. 擷取給定bean執行個體的對象,如果是FactoryBean,則為bean執行個體本身或其建立的對象。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 其它情況暫不做深入研究,感興趣的讀者可以自行閱讀AbstractBeanFactory#doGetBean方法源碼
......
}
return (T) bean;
}
檢視第二點getSingleton方法的實作:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 如果從singletonObjects單例bean緩存中擷取key為beanName的單例bean為空并且該單例bean
// 目前在建立中(在整個工廠内)則從早期已執行個體化的單例bean緩存earlySingletonObjects中
// 檢查beanName的單例對象,如果為空則進一步從singletonFactories單例工廠緩存中擷取beanName為key
// 的BeanFactory,如果BeanFactory不為空則擷取到其管理的單例bean執行個體并将其緩存
// 到earlySingletonObjects對象上,最後從singletonFactories緩存中移除管理該beanName
// 執行個體的BeanFactory對象(解決循環引用)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
幾個重要對象說明:
- singletonObjects:單例bean對象的緩存,ConcurrentHashMap->{beanName:beanInstance}
- earlySingletonObjects: 早期的單例bean對象的緩存,HashMap->{beanName:beanInstance}
- singletonFactories:單例BeanFactory的緩存,HashMap->{beanName:beanFactory}, beanFactory->beanInstance
回到refresh方法的第十二點,最後完成上下文的重新整理操作,調用LifecycleProcessor的onRefresh方法并且釋出最終的ContextRefreshedEvent事件:
protected void finishRefresh() {
// 清除此資源加載器中的所有資源緩存。
clearResourceCaches();
// 初始化此上下文的生命周期處理器DefaultLifecycleProcessor。
initLifecycleProcessor();
// 調用DefaultLifecycleProcessor的onRefresh方法
getLifecycleProcessor().onRefresh();
// 釋出最終的ContextRefreshedEvent事件
publishEvent(new ContextRefreshedEvent(this));
// 如果激活則參與到LiveBeansView MBean中
LiveBeansView.registerApplicationContext(this);
}
子類finishRefresh方法最後啟動相應的WebServer并釋出事件。
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
以上就是Spring Boot啟動流程源碼分析的完整内容,如果有問題歡迎提出!