文章目錄
- 前言
- 1、@SpringBootApplication注解剖析
- 1.1、@SpringBootConfiguration
- 1.2、@EnableAutoConfiguration
- 1.2.1@AutoConfigurationPackage
- 1.2.2 @Import(AutoConfigurationImportSelector.class)
- 1.3@ComponentScan
- 2.SpringApplication.run(啟動類.class,args)方法剖析
- 2.1實體化SpringApplication物件
- 2.2 run(args):呼叫run方法
- 2.2.1run(args)方法——第三步之創建Spring應用背景關系
- 2.2.2run(args)方法——第四步之Spring應用背景關系前置處理
- 2.2.3run(args)方法——第五步之重繪容器
前言
SpringBoot 設計的目的是為了讓你盡可能快的跑起來 Spring 應用程式并且盡可能減少你的組態檔,SpringBoot相對Spring的優點主要有兩個:
1.起步依賴-會將很多jar包按照功能合并成stater整體進行版本管理和參考,解決Spring集成其他框架時jar版本管理問題
2.自動裝配-引入相關的jar包后SpringBoot會自動注冊一些比較關鍵的bean,并進行默認配置,不用我們進行特殊配置,解決Spring重量級XML配置問題,比如整合Mybatis時的SqlSessionFactory
注:其中起步依賴主要是解決版本控制問題,主要設計在于POM檔案,這里主要探究第二優點自動裝配,
SpringBoot啟動依靠的是帶有main方法的啟動類,啟動類的內容可以分為兩個部分一個是啟動類上@SpringBootApplication這個注解;第二部分是main方法里的SpringApplication.run(啟動類.class,args)方法,下面主要就是分析一下這兩部分分別是什么作用?完成了什么功能?怎樣實作的自動裝配?以及SpringBoot的啟動流程分析,
1、@SpringBootApplication注解剖析
@SpringBootApplication是個組合注解包含四個元注解和@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan組成,下面逐個分析,


1.1、@SpringBootConfiguration
@SpringBootConfiguration也是一個組合注解由元注解和@Configuration構成,@Configuration是@Component的一個衍生注解主要作用是標記當前類是個配置類,

1.2、@EnableAutoConfiguration
@EnableAutoConfiguration也是一個組合注解由元注解和@AutoConfigurationPackage、@Import注解構成,Spring中有很多Enable開頭的注解,其作用大都是借助@Import來收集并注冊特定場景相關的bean,@EnableAutoConfiguration的主要作用就是借助@Import來收集并注冊所有符合自動裝配條件的bean,

1.2.1@AutoConfigurationPackage
注:很多人以為@SpringBootApplication可以掃描啟動類當前包及其子包下面的類是由此注解完成的,是錯誤的
@AutoConfigurationPackage由元注解和@Import注解組成

@Import注解匯入了AutoConfigurationPackages.Registrar.class實作了ImportBeanDefinitionRegistrar介面會呼叫registerBeanDefinitions方法

進入AutoConfigurationPackages#register,這里主要為Spring容器里注入了BasePackages的BeanDefinition目的是講啟動類的包路徑傳入容器,官網解釋在后面整合jpa時會用到,這里暫不做探究,
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// 如果已經存在該 BEAN ,則修改其包(package)屬性
// BEAN 就是 AutoConfigurationPackages,用于存盤自動配置包以供稍后參考
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
// 將建構式的第一個引數設定為包名串列
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
// 如果不存在該 BEAN ,則創建一個 Bean ,并進行注冊
} else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
// 將beanClass設定為BasePackages
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
// 將建構式的第一個引數設定為包名串列,也就是BasePackages的建構式
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注冊beanDefinition
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
1.2.2 @Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector類是SpringBoot實作自動裝配的關鍵,AutoConfigurationImportSelector實作了DeferredImportSelector介面會呼叫process和selectImports方法(在何處呼叫會在后面2.2.3講到),其中selectImports方法會回傳一個陣列,陣列中的類都會注冊到Spring容器中
AutoConfigurationImportSelector.AutoConfigurationGroup.class
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 斷言
Assert.state(
deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 獲得 AutoConfigurationEntry 物件
// 核心方法:獲取并過濾全部自動裝配的類
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
// 添加到 autoConfigurationEntries 中
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 添加到 entries 中
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
AutoConfigurationImportSelector.AutoConfigurationGroup.class
public Iterable<Entry> selectImports() {
// 如果為空,則回傳空陣列
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
// 獲得 allExclusions
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream).collect(Collectors.toSet());
// 獲得 processedConfigurations
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
// 從 processedConfigurations 中,移除排除的
processedConfigurations.removeAll(allExclusions);
// 處理,回傳結果
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()) // 排序
.stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) // 創建 Entry 物件
.collect(Collectors.toList()); // 轉換成 List
}
由原始碼可以看到selectImports只是對process中封裝到autoConfigurationEntries的結果進行分組排序等處理后回傳,下面主要看到process中的getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);方法
AutoConfigurationImportSelector.class
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
// 1. 判斷是否開啟注解,如未開啟,回傳空串
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 2. 獲得注解的屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 3. getCandidateConfigurations()用來獲取默認支持的自動配置類名串列
// spring Boot在啟動的時候,使用內部工具類SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,
// 找出其中key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的屬性定義的工廠類名稱,
// 將這些值作為自動配置類匯入到容器中,自動配置類就生效了
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 3.1 //去除重復的配置類,若我們自己寫的starter 可能存在重復的
configurations = removeDuplicates(configurations);
// 4. 如果專案中某些自動配置類,我們不希望其自動配置,我們可以通過EnableAutoConfiguration的exclude或excludeName屬性進行配置,
// 或者也可以在組態檔里通過配置項“spring.autoconfigure.exclude”進行配置,
//找到不希望自動配置的配置類(根據EnableAutoConfiguration注解的一個exclusions屬性)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 4.1 校驗排除類(exclusions指定的類必須是自動配置類,否則拋出例外)
checkExcludedClasses(configurations, exclusions);
// 4.2 從 configurations 中,移除所有不希望自動配置的配置類
configurations.removeAll(exclusions);
// 5. 對所有候選的自動配置類進行篩選,根據專案pom.xml檔案中加入的依賴檔案篩選出最終符合當前專案運行環境對應的自動配置類
//@ConditionalOnClass : 某個class位于類路徑上,才會實體化這個Bean,
//@ConditionalOnMissingClass : classpath中不存在該類時起效
//@ConditionalOnBean : DI容器中存在該型別Bean時起效
//@ConditionalOnMissingBean : DI容器中不存在該型別Bean時起效
//@ConditionalOnSingleCandidate : DI容器中該型別Bean只有一個或@Primary的只有一個時起效
//@ConditionalOnExpression : SpEL運算式結果為true時
//@ConditionalOnProperty : 引數設定或者值一致時起效
//@ConditionalOnResource : 指定的檔案存在時起效
//@ConditionalOnJndi : 指定的JNDI存在時起效
//@ConditionalOnJava : 指定的Java版本存在時起效
//@ConditionalOnWebApplication : Web應用環境下起效
//@ConditionalOnNotWebApplication : 非Web應用環境下起效
//要判斷@Conditional是否滿足
// 如@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })表示需要在類路徑中存在SqlSessionFactory.class、SqlSessionFactoryBean.class這兩個類才能完成自動注冊,
configurations = filter(configurations, autoConfigurationMetadata);
// 6. 將自動配置匯入事件通知監聽器
//當AutoConfigurationImportSelector過濾完成后會自動加載類路徑下Jar包中META-INF/spring.factories檔案中 AutoConfigurationImportListener的實作類,
// 并觸發fireAutoConfigurationImportEvents事件,
fireAutoConfigurationImportEvents(configurations, exclusions);
// 7. 創建 AutoConfigurationEntry 物件
return new AutoConfigurationEntry(configurations, exclusions);
}
1.3@ComponentScan
這個注解才是@SpringBootApplication會默認掃描啟動類所在包以及子包路徑下全部類的原因
2.SpringApplication.run(啟動類.class,args)方法剖析
SpringApplication#run主要完成的事件可以分成兩部分1.實體化SpringApplication物件2. run(args):呼叫run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//SpringApplication的啟動由兩部分組成:
//1. 實體化SpringApplication物件
//2. run(args):呼叫run方法
return new SpringApplication(primarySources).run(args);
}

2.1實體化SpringApplication物件
在實體化SpringApplication中設定的初始化器和監聽器都是在/META-INF/spring.factories 中獲取的
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//專案啟動類 SpringbootDemoApplication.class設定為屬性存盤起來
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//設定應用型別是SERVLET應用(Spring 5之前的傳統MVC應用)還是REACTIVE應用(Spring 5開始出現的WebFlux互動式應用)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 設定初始化器(Initializer),最后會呼叫這些初始化器
//所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實作類,在Spring背景關系被重繪之前進行初始化的操作
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 設定監聽器(Listener)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 初始化 mainApplicationClass 屬性:用于推斷并設定專案main()方法啟動的主程式啟動類
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 run(args):呼叫run方法
這里一個分為九步,最核心的是3、4、5下面會逐一介紹:
- 獲取并啟動監聽器,監聽器也是在spring.factories中獲取的,
- 專案運行環境Environment的預配置
- 創建Spring容器
- Spring容器前置處理,這一步主要是在容器重繪之前的準備動作,包含一個非常關鍵的操作:將啟動類注入容器,為后續開啟自動化配置奠定基礎,
- 重繪容器
- Spring容器后置處理,擴展介面,設計模式中的模板方法,默認為空實作,
- 向監聽器發出結束執行的事件通知
- 執行Runners
- 向監聽器發布應用背景關系就緒事件
public ConfigurableApplicationContext run(String... args) {
// 創建 StopWatch 物件,并啟動,StopWatch 主要用于簡單統計 run 啟動程序的時長,
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化應用背景關系和例外報告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 屬性
configureHeadlessProperty();
// (1)獲取并啟動監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 創建 ApplicationArguments 物件 初始化默認應用引數類
// args是啟動Spring應用的命令列引數,該引數可以在Spring應用中被訪問,如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//(2)專案運行環境Environment的預配置
// 創建并配置當前SpringBoot應用將要使用的Environment
// 并遍歷呼叫所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 準備Banner列印器 - 就是啟動Spring Boot的時候列印在console上的ASCII藝術字體
Banner printedBanner = printBanner(environment);
// (3)創建Spring容器
context = createApplicationContext();
// 獲得例外報告器 SpringBootExceptionReporter 陣列
//這一步的邏輯和實體化初始化器和監聽器的一樣,
// 都是通過呼叫 getSpringFactoriesInstances 方法來獲取配置的例外類名稱并實體化所有的例外處理類,
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// (4)Spring容器前置處理
//這一步主要是在容器重繪之前的準備動作,包含一個非常關鍵的操作:將啟動類注入容器,為后續開啟自動化配置奠定基礎,
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// (5):重繪容器
refreshContext(context);
// (6):Spring容器后置處理
//擴展介面,設計模式中的模板方法,默認為空實作,
// 如果有自定義需求,可以重寫該方法,比如列印一些啟動結束log,或者一些其它后置處理
afterRefresh(context, applicationArguments);
// 停止 StopWatch 統計時長
stopWatch.stop();
// 列印 Spring Boot 啟動的時長日志,
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// (7)發出結束執行的事件通知
listeners.started(context);
// (8):執行Runners
//用于呼叫專案中自定義的執行器XxxRunner類,使得在專案啟動完成后立即執行一些特定程式
//Runner 運行器用于在服務啟動時進行一些業務初始化操作,這些操作只在服務啟動后執行一次,
//Spring Boot提供了ApplicationRunner和CommandLineRunner兩種服務介面
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 如果發生例外,則進行處理,并拋出 IllegalStateException 例外
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// (9)發布應用背景關系就緒事件
//表示在前面一切初始化啟動都沒有問題的情況下,使用運行監聽器SpringApplicationRunListener持續運行配置好的應用背景關系ApplicationContext,
// 這樣整個Spring Boot專案就正式啟動完成了,
try {
listeners.running(context);
} catch (Throwable ex) {
// 如果發生例外,則進行處理,并拋出 IllegalStateException 例外
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//回傳容器
return context;
}
2.2.1run(args)方法——第三步之創建Spring應用背景關系
這里根據實體SpringApplication時獲取的應用型別來創建不同的應用背景關系物件
SpringApplication.class
protected ConfigurableApplicationContext createApplicationContext() {
// 根據 webApplicationType 型別,獲得 ApplicationContext 型別
// 這里創建容器的型別 還是根據webApplicationType進行判斷的,
// 該型別為SERVLET型別,所以會通過反射裝載對應的位元組碼,
// 也就是AnnotationConfigServletWebServerApplicationContext
// 先判斷有沒有指定的實作類
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
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);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
}
}
// 創建 ApplicationContext 物件
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
2.2.2run(args)方法——第四步之Spring應用背景關系前置處理
這塊會對整個背景關系進行一個預處理,比如觸發監聽器的回應事件、加載資源、設定背景關系環境等等
SpringApplication.class
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設定容器環境,包括各種變數
context.setEnvironment(environment);
//設定背景關系的 bean 生成器和資源加載器
postProcessApplicationContext(context);
//執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實體)
applyInitializers(context);
//觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//記錄啟動日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//注冊啟動引數bean,這里將容器指定的引數封裝成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);
}
// Load the sources
// 加載所有資源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加載我們的啟動類,將啟動類注入容器,為后續開啟自動化配置奠定基礎
load(context, sources.toArray(new Object[0]));
//觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
在前置處理中最核心的一步是加載我們的啟動類,將啟動類注入容器,為后續開啟自動化配置奠定基礎load(context, sources.toArray(new Object[0]));
BeanDefinitionLoader.class
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
// 如果是 Class 型別,則使用 AnnotatedBeanDefinitionReader 執行加載
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
// 如果是 Resource 型別,則使用 XmlBeanDefinitionReader 執行加載
if (source instanceof Resource) {
return load((Resource) source);
}
// 如果是 Package 型別,則使用 ClassPathBeanDefinitionScanner 執行加載
if (source instanceof Package) {
return load((Package) source);
}
// 如果是 CharSequence 型別,則各種嘗試去加載
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
// 無法處理的型別,拋出 IllegalArgumentException 例外
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
2.2.3run(args)方法——第五步之重繪容器
這里重繪容器最終呼叫的是AbstractApplication#refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 第一步 重繪前的預處理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 第二步 1.創建BeanFactory實體,默認實作是DefaultListableBeanFactory
// 2.決議XML中的<bean>為BeanDefition 并注冊到 BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 第三步 BeanFactory的預準備?作(BeanFactory進??些設定,?如context的類加載器等)
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 第四步 BeanFactory準備作業完成后的后置處理作業,鉤子方法,等子類重寫
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 第五步 實體化并調?實作了BeanFactoryPostProcessor接?的Bean
// 提前初始化工廠后置處理器bean,并呼叫postProcessBeanFactory方法
//其中BeanFactoryPostProcessor比較重要的一個ConfigurationClassPostProcessor在這里呼叫,
//用來遍歷BeanDefinitionRegistry中現有的BeanDefinition決議@Import、@Configuration
// 、@ComponentScan等注解將注解覆寫到的類也注冊到BeanDefinitionRegistry中
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 第六步 注冊BeanPostProcessor(Bean的后置處理器),在創建bean的前后等執
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 第七步 初始化MessageSource組件(做國際化功能;訊息系結,訊息決議);
initMessageSource();
// Initialize event multicaster for this context.
// 第八步 初始化事件派發器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 第九步 ?類重寫這個?法,在容器重繪的時候可以?定義邏輯,鉤子方法
onRefresh();
// Check for listener beans and register them.
// 第十步 注冊應?的監聽器,就是注冊實作了ApplicationListener接?的監聽器bean
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 第十一步 初始化所有剩下的?懶加載的單例bean
//1).初始化創建?懶加載?式的單例Bean實體(未設定屬性)
//2).填充屬性
//3) .如果bean實作了Aware相關介面,則呼叫Aware介面的實作方法
//4) .呼叫BeanPostProcessor處理器的前置方法
//5).初始化?法調?(?如調?afterPropertiesSet?法、init-method?法)
//6).調?BeanPostProcessor(后置處理器)對實體bean進?后置處
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 第十二步 完成context的重繪,主要是調?LifecycleProcessor的onRefresh()?法,并且發布事件 (ContextRefreshedEvent)
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();
}
}
}
這也是Spring容器啟動的經典方法這里就不每個步驟逐一過了,只重點關注和SpringBoot自動裝配相關的步驟——第五步 實體化并調?實作了BeanFactoryPostProcessor接?的Bean,就是在這一步決議的@SpringBootApplication這個組合注解,BeanFactoryPostProcessor比較重要的一個ConfigurationClassPostProcessor在這里呼叫,用來遍歷BeanDefinitionRegistry中現有的BeanDefinition決議@Import、@Configuration 、@ComponentScan等注解將注解覆寫到的類也注冊到BeanDefinitionRegistry中,
a.進入ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor.class
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 核心方法
processConfigBeanDefinitions(registry);
}
b.進入ConfigurationClassPostProcessor#processConfigBeanDefinitions,這里遍歷BeanDefinitionRegistry現有的全部類不包含@Configuration的類不會進行決議,這也是為什么配置類需要加@Configuration的原因
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//如果不存在@Configuration直接return
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 核心決議方法
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
c.進入ConfigurationClassParser#parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 注解決議BeanDefinition核心方法
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
d.進入ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
// 決議核心方法
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
e.進入ConfigurationClassParser#doProcessConfigurationClass,在這里決議@PropertySource、@ComponentScan、@Import、@Bean、@ImportResource等注解,并將其覆寫的資源或類加載到容器背景關系中,每個注解的具體決議細節這里就不深探討了,主要梳理流程
ConfigurationClassParser.class
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
//決議@PropertySource注解
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 決議@ComponentScan注解
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 決議@Import注解
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 決議@ImportResource注解
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 決議@Bean注解
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
f.此處簡單過一下@Import注解的決議程序,驗證一下1.2.2 @Import(AutoConfigurationImportSelector.class)中process和selectImports方法的呼叫進入ConfigurationClassParser#processImports

在進入ConfigurationClassParser#handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
// 核心方法
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
進入ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
//getImports()中呼叫了AutoConfigurationImportSelector.AutoConfigurationGroup.class中的process和selectImports
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
進入ConfigurationClassParser.DeferredImportSelectorGrouping#getImports方法此處呼叫了AutoConfigurationImportSelector.AutoConfigurationGroup的process和selectImports

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/260562.html
標籤:java
下一篇:java學習日記
