EnableAutoConfiguration 是SpringBoot的Enable系列中一個(gè)比較基礎(chǔ)的的功能模塊,現(xiàn)在我們就來(lái)學(xué)習(xí)如何使用,以及分析源碼學(xué)習(xí)其工作原理
EnableAutoConfiguration 從名字也可以很容易看出來(lái)其功能是能夠自動(dòng)裝配配置,在SpringBoot中如果需要為其他人提供SDK等接口使用,使用方本身必須實(shí)例化接口類(lèi)才可調(diào)用,如果每一個(gè)使用方都單獨(dú)去實(shí)例化該接口,必然導(dǎo)致使用成本的增加,EnableAutoConfiguration就能很好的解決這個(gè)問(wèn)題,使用方通過(guò)這個(gè)就可以直接使用,避免額外操作。
1、EnableAutoConfiguration 源碼學(xué)習(xí)
先提個(gè)問(wèn)題,如果現(xiàn)在只能使用Spring Framework,該如何實(shí)現(xiàn)類(lèi)似的功能呢?
或許能想到的只有BPP,BeanPostProcessor或者BeanFactoryPostProcessor,只是他們處理的范疇不一樣,BeanPostProcessor更多的是處理單個(gè)bean,而B(niǎo)eanFactoryPostProcessor是處理context上下文的
Spring包含了多種類(lèi)型的BPP,在spring的生命周期的多個(gè)位置提供了對(duì)外的鉤子便于擴(kuò)展更多功能,關(guān)于BPP可以看看BPP的內(nèi)容
在官方文檔中,對(duì)BeanFactoryPostProcessor方法的簡(jiǎn)述也說(shuō)的非常清楚,Modify the application context's internal bean factory after its standard initialization. All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
事實(shí)上,SpringBoot也確實(shí)是這樣干的,ConfigurationClassPostProcessor 就是實(shí)例化了一個(gè)BeanDefinitionRegistryPostProcessor,從而擁有了修改context上下文的bean信息以及注冊(cè)bean的功能
如下圖由Spring的BPP處理器調(diào)用到ConfigurationClassPostProcessor,然后來(lái)到了AutoConfigurationImportSelector 類(lèi)中

1.1、ConfigurationClassPostProcessor 處理
先了解下ConfigurationClassPostProcessor 這個(gè)BPP是如何被注入到spring容器的
在springboot啟動(dòng)學(xué)習(xí)筆記中 已經(jīng)介紹了spring的context上下文創(chuàng)建是由context = createApplicationContext(); 實(shí)現(xiàn)的,深入該代碼直到AnnotationConfigUtils 類(lèi)可以發(fā)現(xiàn)其操作是如果沒(méi)發(fā)現(xiàn)org.springframework.context.annotation.internalCommonAnnotationProcessor這個(gè)bean的name,那就添加一個(gè)ConfigurationClassPostProcessor bean,具體如下圖

這樣我們就清楚了ConfigurationClassPostProcessor 這個(gè)類(lèi)是如何裝載進(jìn)spring容器中的,接下來(lái)就是ConfigurationClassPostProcessor這個(gè)類(lèi)具體的調(diào)用操作了
ConfigurationClassPostProcessor 類(lèi)
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
// 遍歷spring容器所有的beanName信息,此時(shí)還未裝載業(yè)務(wù)bean,
// 只有spring&springboot本身框架層次需要的一些特定bean存在(特別注意包含主啟動(dòng)類(lèi))
// 這點(diǎn)在之前的關(guān)于springboot的啟動(dòng)學(xué)習(xí)筆記中已經(jīng)介紹了
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
// 確認(rèn)該beandefinition是否存在org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass 鍵值對(duì)信息
// 如果存在則假定是已經(jīng)經(jīng)過(guò)配置類(lèi)處理過(guò)了
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 否則檢查一下當(dāng)前的beandefinition是否符合config 配置類(lèi)
// 具體實(shí)現(xiàn)原理就是獲取到bean的注解信息,然后查看是否存在 @Configuration 注解類(lèi)或者 @Bean 注解
// 如果有,則返回true
// 當(dāng)然在這里只會(huì)有主啟動(dòng)類(lèi)才包含了@Configuration 的信息
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 一般情況下,到這里只會(huì)有主啟動(dòng)類(lèi)一個(gè)configCandidates信息存在
// Sort by previously determined @Order value, if applicable
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
// 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 && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
// 如果當(dāng)前類(lèi)不包含beanName 生成器 同時(shí) context包含了單一的beanName生成器
// 設(shè)置當(dāng)前bean的生成器信息
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// 生成配置解析類(lèi)parses,開(kāi)始解析每一個(gè)包含了@Configuration 的類(lèi)
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
parser.parse(candidates);
// 關(guān)鍵的地方來(lái)了,這里就會(huì)去解析真正包含了@Configuration 的類(lèi)
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
// 所有通過(guò)@Configuration 裝載進(jìn)來(lái)的類(lèi)集合
configClasses.removeAll(alreadyParsed);
// 已經(jīng)裝載的就移除掉
// 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());
}
// reader是配置類(lèi)裝載類(lèi)beandefinition
this.reader.loadBeanDefinitions(configClasses);
// 裝載到Spring容器中,包含了那些使用@Bean的類(lèi)信息
alreadyParsed.addAll(configClasses);
......
// 到這里就可以認(rèn)為@Configuration 的導(dǎo)入基本完成了
}
真正進(jìn)入到@Configuration 解析的入口處代碼
ConfigurationClassParser 類(lèi)

在經(jīng)過(guò)processDeferredImportSelectors方法調(diào)用的之前,已經(jīng)經(jīng)過(guò)了parse處理,明確了@Configuration 包含的ImportSelectors 信息
如果需要自定義該注解則一定也要實(shí)現(xiàn)利用@Import注解導(dǎo)入的ImportSelector 實(shí)現(xiàn)類(lèi)
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
// 對(duì)獲取的DeferredImportSelectorHolder 排序后進(jìn)行遍歷操作
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
// configClass 就是主啟動(dòng)類(lèi)
try {
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
// 所有的需要導(dǎo)入的import類(lèi)的selectImports方法執(zhí)行,
// 這個(gè)里面就是EnableAutoConfigurationImportSelector
// 具體的EnableAutoConfigurationImportSelector里面的selectImports后面說(shuō)
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
// 把獲取的配置類(lèi)信息進(jìn)一步迭代處理,因?yàn)榇嬖谠陬?lèi)中包含了配置類(lèi)的情況
// 不過(guò)需要注意,這時(shí)候并未往spring容器中注入
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
1.2、EnableAutoConfigurationImportSelector 的 selectImports 執(zhí)行
來(lái)到AutoConfigurationImportSelector類(lèi)
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
// 如果注解原信息未激活,則不可用,直接返回空
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 利用SpringFactoriesLoader 獲取系統(tǒng)中所有的META-INF/spring.factories 的 EnableAutoConfiguration 的鍵值對(duì)信息,其中就包含了上面我們自定義的類(lèi)信息
configurations = removeDuplicates(configurations);
// 存在多處地方可能注冊(cè)了相同的類(lèi)信息,去重處理
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 匹配出包含exclude、excludeName 的列表信息,后續(xù)移除該config
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
// 總之經(jīng)過(guò)各種操作,最后產(chǎn)出了可用的配置類(lèi)列表
fireAutoConfigurationImportEvents(configurations, exclusions);
// 配置導(dǎo)入的事件觸發(fā)
return configurations.toArray(new String[configurations.size()]);
// 返回最后的配置類(lèi)列表
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
1.3、總結(jié)
到這里對(duì)EnableAutoConfiguration的源碼學(xué)習(xí)就算是結(jié)束了,利用Spring對(duì)外的BeanFactoryPostProcessor的鉤子ConfigurationClassPostProcessor去解析出@Import引入的類(lèi),然后解析出所有被@Configuration的對(duì)象,然后注冊(cè)到spring容器中,這樣就完成了整個(gè)的自動(dòng)裝載過(guò)程
2、DEMO

如上圖,可以發(fā)現(xiàn)在資源根目錄下存放了一個(gè)"META-INF/spring.factories"的文件,里面的內(nèi)容也很簡(jiǎn)單
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.boot.api.CustomAutoConfiguration
CustomAutoConfiguration 類(lèi)代碼
@Configuration
public class CustomAutoConfiguration {
@Bean
public ApiStudent apiStudent() {
ApiStudent apiStudent = new ApiStudent();
// ApiStudent 只有一個(gè)name字段的基礎(chǔ)POJO
apiStudent.setName("auto-config");
return apiStudent;
}
}
非常簡(jiǎn)單的一個(gè)注冊(cè)bean到spring的樣例
現(xiàn)在我們?cè)诹硪粋€(gè)服務(wù)中引入該服務(wù),然后直接通過(guò)@resource 注解就可以引用到ApiStudent這個(gè)bean了
@Resource
private ApiStudent apiStudent;
@GetMapping("/custom-autoconfig")
@ResponseBody
public String autoConfig() {
return apiStudent.getName();
}

可以看出網(wǎng)頁(yè)上輸出的內(nèi)容就是起初在CustomAutoConfiguration中為apiStudent這個(gè)bean設(shè)置的屬性值
到此demo就結(jié)束了,非常的基礎(chǔ)的一個(gè)樣例,在實(shí)際應(yīng)用中也是如此,也使用的非常頻繁。