微信公衆號:吉姆餐廳ak 學習更多源碼知識,歡迎關注。 java
![]()
SpringBoot2 | SpringBoot啓動流程源碼分析(一)web
SpringBoot2 | SpringBoot啓動流程源碼分析(二)spring
SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)設計模式
SpringBoot2 | SpringBoot Environment源碼分析(四)bash
SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)微信
SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)app
SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)源碼分析
SpringBoot2 | Spring AOP 原理源碼深度剖析(八)post
SpringBoot2 | SpingBoot FilterRegistrationBean 註冊組件 | FilterChain 責任鏈源碼分析(九)性能
SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar (十)
SpringBoot2 | Spring 核心擴展接口 | 核心擴展方法總結(十一)
繼續上一篇博客的啓動流程分析。
在上一篇SpringBoot | SpringBoot2 | SpringBoot2啓動流程源碼分析(一)中咱們提到springBoot啓動流程大體有如下7點:
上一篇博客中分析了前面兩點,今天繼續分析後面四點。
context = createApplicationContext();
複製代碼
繼續跟進該方法:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
複製代碼
上面能夠看出,這裏建立容器的類型 仍是根據webApplicationType
進行判斷的,上一篇已經講述了該變量如何賦值的過程。由於該類型爲SERVLET
類型,因此會經過反射裝載對應的字節碼,以下:
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
複製代碼
該對象是springBoot2建立的容器,後續全部的操做都會基於該容器。
注意:在 springBoot2版本之前,該容器的名稱爲 AnnotationConfigServletWebServerApplicationContext,在最新的版本中才更名爲 AnnotationConfigServletWebServerApplicationContext。 下面是該類的結構圖:
具體做用後面會詳細介紹。 ![]()
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
複製代碼
這裏仍是以一樣的方式獲取 spring.factories文件中的指定類:
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
複製代碼
該類主要是在項目啓動失敗以後,打印log:
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters,
Throwable failure) {
try {
for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
//上報錯誤log
registerLoggedException(failure);
return;
}
}
}
catch (Throwable ex) {
// Continue with normal handling of the original failure
}
if (logger.isErrorEnabled()) {
logger.error("Application run failed", failure);
registerLoggedException(failure);
}
}
複製代碼
這一步主要是在容器刷新以前的準備動做。包含一個很是關鍵的操做:將啓動類注入容器,爲後續開啓自動化配置奠基基礎。
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
複製代碼
繼續跟進該方法:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設置容器環境,包括各類變量
context.setEnvironment(environment);
//執行容器後置處理
postProcessApplicationContext(context);
//執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實例)
applyInitializers(context);
//發送容器已經準備好的事件,通知各監聽器
listeners.contextPrepared(context);
//打印log
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//註冊啓動參數bean,這裏將容器指定的參數封裝成bean,注入容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
//設置banner
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[0]));
//發佈容器已加載事件。
listeners.contextLoaded(context);
}
複製代碼
來看一下上面的幾個核心處理。
1)容器的後置處理:
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());
}
}
}
複製代碼
這裏默認不執行任何邏輯,由於beanNameGenerator
和resourceLoader
默認爲空。之因此這樣作,是springBoot留給咱們的擴展處理方式,相似於這樣的擴展,spring中也有不少。
2)加載啓動指定類(重點) 這裏會將咱們的啓動類加載spring容器beanDefinitionMap
中,爲後續springBoot 自動化配置奠基基礎,springBoot
爲咱們提供的各類註解配置也與此有關。
load(context, sources.toArray(new Object[0]));
複製代碼
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
複製代碼
這裏參數即爲咱們項目啓動時傳遞的參數:SpringApplication.run(SpringBootApplication.class, args);
因爲咱們指定了啓動類,因此上面也就是加載啓動類到容器。
須要注意的是,springBoot2會優先選擇
groovy加載方式,找不到再選用
java方式。或許
groovy動態加載class文件的性能更勝一籌
。
private int load(Class<?> source) {
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
//以註解的方式,將啓動類bean信息存入beanDefinitionMap
this.annotatedReader.register(source);
return 1;
}
return 0;
}
複製代碼
上面代碼中啓動類被加載到 beanDefinitionMap中,後續該啓動類將做爲開啓自動化配置的入口,後面一篇文章我會詳細的分析,啓動類是如何加載,以及自動化配置開啓的詳細流程。
3)通知監聽器,容器已準備就緒
listeners.contextLoaded(context);
複製代碼
主仍是針對一些日誌等監聽器的響應處理。
執行到這裏,springBoot相關的處理工做已經結束,接下的工做就交給了spring。
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 刷新上下文環境
* 初始化上下文環境,對系統的環境變量或者系統屬性進行準備和校驗
* 如環境變量中必須設置某個值才能運行,不然不能運行,這個時候能夠在這裏加這個校驗,
* 重寫initPropertySources方法就行了
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/**
* 初始化BeanFactory,解析XML,至關於以前的XmlBeanFactory的操做,
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
/**
* 爲上下文準備BeanFactory,即對BeanFactory的各類功能進行填充,如經常使用的註解@Autowired @Qualifier等
* 設置SPEL表達式#{key}的解析器
* 設置資源編輯註冊器,如PerpertyEditorSupper的支持
* 添加ApplicationContextAwareProcessor處理器
* 在依賴注入忽略實現*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
* 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的實例注入進去
*/
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
/**
* 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
* 激活各類BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 執行對應的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/**
* 註冊攔截Bean建立的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意二者的區別
* 注意,這裏僅僅是註冊,並不會執行對應的方法,將在bean的實例化時執行對應的方法
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
/**
* 初始化上下文中的資源文件,如國際化文件的處理等
*/
initMessageSource();
// Initialize event multicaster for this context.
/**
* 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
/**
* 給子類擴展初始化其餘Bean
*/
onRefresh();
// Check for listener beans and register them.
/**
* 在全部bean中查找listener bean,而後註冊到廣播器中
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
* 設置轉換器
* 註冊一個默認的屬性值解析器
* 凍結全部的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
* 初始化剩餘的非惰性的bean,即初始化非延遲加載的bean
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
/**
* 初始化生命週期處理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring啓動的時候調用start方法開始生命週期,
* spring關閉的時候調用stop方法來結束生命週期,一般用來配置後臺程序,啓動有一直運行,如一直輪詢kafka
* 啓動全部實現了Lifecycle接口的類
* 經過spring的事件發佈機制發佈ContextRefreshedEvent事件,以保證對應的監聽器作進一步的處理,即對那種在spring啓動後須要處理的一些類,這些類實現了
* ApplicationListener<ContextRefreshedEvent> ,這裏就是要觸發這些類的執行(執行onApplicationEvent方法)另外,spring的內置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命週期處理器lifeCycleProcessor刷新過程,同時發出ContextRefreshEvent通知其餘人
*/
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(); } } 複製代碼
refresh
方法在spring整個源碼體系中舉足輕重,是實現 ioc 和 aop的關鍵。上述流程,不是一篇博文可以展現清楚的,因此這裏暫時不作展開。後續會有詳細的介紹。
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
複製代碼
擴展接口,設計模式中的模板方法,默認爲空實現。若是有自定義需求,能夠重寫該方法。好比打印一些啓動結束log,或者一些其它後置處理。
springBoot2
啓動流程到這裏就結束了。後續會對springBoot2
的經常使用註解,及一些核心類進行介紹。
SpringBoot2 | SpringBoot啓動流程源碼分析(一)
SpringBoot2 | SpringBoot啓動流程源碼分析(二)
SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)
SpringBoot2 | SpringBoot Environment源碼分析(四)
SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)