EnableAutoConfiguration 是SpringBoot的Enable系列中一個比較基礎(chǔ)的的功能模塊毫蚓,現(xiàn)在我們就來學(xué)習(xí)如何使用,以及分析源碼學(xué)習(xí)其工作原理
EnableAutoConfiguration 從名字也可以很容易看出來其功能是能夠自動裝配配置,在SpringBoot中如果需要為其他人提供SDK等接口使用,使用方本身必須實例化接口類才可調(diào)用蛹找,如果每一個使用方都單獨(dú)去實例化該接口蜕便,必然導(dǎo)致使用成本的增加劫恒,EnableAutoConfiguration就能很好的解決這個問題,使用方通過這個就可以直接使用轿腺,避免額外操作两嘴。
1、EnableAutoConfiguration 源碼學(xué)習(xí)
先提個問題族壳,如果現(xiàn)在只能使用Spring Framework憔辫,該如何實現(xiàn)類似的功能呢?
或許能想到的只有BPP仿荆,BeanPostProcessor或者BeanFactoryPostProcessor贰您,只是他們處理的范疇不一樣,BeanPostProcessor更多的是處理單個bean拢操,而BeanFactoryPostProcessor是處理context上下文的
Spring包含了多種類型的BPP锦亦,在spring的生命周期的多個位置提供了對外的鉤子便于擴(kuò)展更多功能,關(guān)于BPP可以看看BPP的內(nèi)容
在官方文檔中令境,對BeanFactoryPostProcessor方法的簡述也說的非常清楚杠园,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.
事實上,SpringBoot也確實是這樣干的舔庶,ConfigurationClassPostProcessor 就是實例化了一個BeanDefinitionRegistryPostProcessor抛蚁,從而擁有了修改context上下文的bean信息以及注冊bean的功能
如下圖由Spring的BPP處理器調(diào)用到ConfigurationClassPostProcessor,然后來到了AutoConfigurationImportSelector 類中
1.1惕橙、ConfigurationClassPostProcessor 處理
先了解下ConfigurationClassPostProcessor 這個BPP是如何被注入到spring容器的
在springboot啟動學(xué)習(xí)筆記中 已經(jīng)介紹了spring的context上下文創(chuàng)建是由context = createApplicationContext();
實現(xiàn)的瞧甩,深入該代碼直到AnnotationConfigUtils 類可以發(fā)現(xiàn)其操作是如果沒發(fā)現(xiàn)org.springframework.context.annotation.internalCommonAnnotationProcessor
這個bean的name,那就添加一個ConfigurationClassPostProcessor bean弥鹦,具體如下圖
這樣我們就清楚了ConfigurationClassPostProcessor 這個類是如何裝載進(jìn)spring容器中的肚逸,接下來就是ConfigurationClassPostProcessor這個類具體的調(diào)用操作了
ConfigurationClassPostProcessor 類
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
// 遍歷spring容器所有的beanName信息,此時還未裝載業(yè)務(wù)bean彬坏,
// 只有spring&springboot本身框架層次需要的一些特定bean存在(特別注意包含主啟動類)
// 這點(diǎn)在之前的關(guān)于springboot的啟動學(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 鍵值對信息
// 如果存在則假定是已經(jīng)經(jīng)過配置類處理過了
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 配置類
// 具體實現(xiàn)原理就是獲取到bean的注解信息吼虎,然后查看是否存在 @Configuration 注解類或者 @Bean 注解
// 如果有,則返回true
// 當(dāng)然在這里只會有主啟動類才包含了@Configuration 的信息
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 一般情況下苍鲜,到這里只會有主啟動類一個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)前類不包含beanName 生成器 同時 context包含了單一的beanName生成器
// 設(shè)置當(dāng)前bean的生成器信息
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// 生成配置解析類parses思灰,開始解析每一個包含了@Configuration 的類
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)鍵的地方來了,這里就會去解析真正包含了@Configuration 的類
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
// 所有通過@Configuration 裝載進(jìn)來的類集合
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是配置類裝載類beandefinition
this.reader.loadBeanDefinitions(configClasses);
// 裝載到Spring容器中混滔,包含了那些使用@Bean的類信息
alreadyParsed.addAll(configClasses);
......
// 到這里就可以認(rèn)為@Configuration 的導(dǎo)入基本完成了
}
真正進(jìn)入到@Configuration 解析的入口處代碼
ConfigurationClassParser 類
在經(jīng)過processDeferredImportSelectors方法調(diào)用的之前洒疚,已經(jīng)經(jīng)過了parse處理歹颓,明確了@Configuration 包含的ImportSelectors 信息
如果需要自定義該注解則一定也要實現(xiàn)利用@Import注解導(dǎo)入的ImportSelector 實現(xiàn)類
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
// 對獲取的DeferredImportSelectorHolder 排序后進(jìn)行遍歷操作
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
// configClass 就是主啟動類
try {
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
// 所有的需要導(dǎo)入的import類的selectImports方法執(zhí)行,
// 這個里面就是EnableAutoConfigurationImportSelector
// 具體的EnableAutoConfigurationImportSelector里面的selectImports后面說
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
// 把獲取的配置類信息進(jìn)一步迭代處理油湖,因為存在在類中包含了配置類的情況
// 不過需要注意巍扛,這時候并未往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í)行
來到AutoConfigurationImportSelector類
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 的鍵值對信息,其中就包含了上面我們自定義的類信息
configurations = removeDuplicates(configurations);
// 存在多處地方可能注冊了相同的類信息喊括,去重處理
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)過各種操作郑什,最后產(chǎn)出了可用的配置類列表
fireAutoConfigurationImportEvents(configurations, exclusions);
// 配置導(dǎo)入的事件觸發(fā)
return configurations.toArray(new String[configurations.size()]);
// 返回最后的配置類列表
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
1.3府喳、總結(jié)
到這里對EnableAutoConfiguration的源碼學(xué)習(xí)就算是結(jié)束了,利用Spring對外的BeanFactoryPostProcessor的鉤子ConfigurationClassPostProcessor去解析出@Import引入的類蘑拯,然后解析出所有被@Configuration的對象钝满,然后注冊到spring容器中,這樣就完成了整個的自動裝載過程
2申窘、DEMO
如上圖弯蚜,可以發(fā)現(xiàn)在資源根目錄下存放了一個"META-INF/spring.factories"的文件,里面的內(nèi)容也很簡單
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.demo.boot.api.CustomAutoConfiguration
CustomAutoConfiguration 類代碼
@Configuration
public class CustomAutoConfiguration {
@Bean
public ApiStudent apiStudent() {
ApiStudent apiStudent = new ApiStudent();
// ApiStudent 只有一個name字段的基礎(chǔ)POJO
apiStudent.setName("auto-config");
return apiStudent;
}
}
非常簡單的一個注冊bean到spring的樣例
現(xiàn)在我們在另一個服務(wù)中引入該服務(wù)剃法,然后直接通過@resource 注解就可以引用到ApiStudent這個bean了
@Resource
private ApiStudent apiStudent;
@GetMapping("/custom-autoconfig")
@ResponseBody
public String autoConfig() {
return apiStudent.getName();
}
可以看出網(wǎng)頁上輸出的內(nèi)容就是起初在CustomAutoConfiguration中為apiStudent這個bean設(shè)置的屬性值
到此demo就結(jié)束了熟吏,非常的基礎(chǔ)的一個樣例,在實際應(yīng)用中也是如此玄窝,也使用的非常頻繁。