淺析SpringBoot自動裝配的實現(xiàn)
背景
眾所周知,如下即可啟動一個最簡單的Spring應(yīng)用。查看@SpringBootApplication注解的源碼,發(fā)現(xiàn)這個注解上有一個重要的注解@EnableAutoConfiguration,而這個注解就是SpringBoot實現(xiàn)自動裝配的基礎(chǔ)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}@EnableAutoConfiguration
EnableAutoConfiguration注解上通過@Import引入了兩個類,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector及org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar。通過@Import標(biāo)注的類,會在解析@Import所在的配置類時,將標(biāo)注類引入容器解析,并進(jìn)行注冊。
有眾多的組件都是通過在配置類上加@EnableAutoConfiguration注解將組件引入的
ImportBeanDefinitionRegistrar實現(xiàn)了org.springframework.context.annotation.ImportBeanDefinitionRegistrar及org.springframework.boot.context.annotation.DeterminableImportsAutoConfigurationImportSelector實現(xiàn)了org.springframework.context.annotation.DeferredImportSelector
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
....
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
...
}解析
起始
通過BeanFactoryPostProcessor對需要注冊的Bean進(jìn)行解析。即org.springframework.context.support.AbstractApplicationContext#refresh,在AbstractApplicationContext#invokeBeanFactoryPostProcessors方法調(diào)用時,就開始了對服務(wù)配置bean的解析,為對象的生成做準(zhǔn)備
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
try {
...
invokeBeanFactoryPostProcessors(beanFactory);
}
catch (BeansException ex) {
finally {
}
}具體解析
調(diào)用org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors,通過獲取到的BeanFactoryPostProcessor實現(xiàn)類對各種配置類進(jìn)行解析,具體的BeanFactoryPostProcessor解析后面我們在具體分析。
這里有一個很重要的類org.springframework.context.annotation.ConfigurationClassPostProcessor,首先會調(diào)用postProcessBeanDefinitionRegistry方法
// ConfigurationClassPostProcessor類部門源碼
/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@Override
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)) {
"postProcessBeanFactory already called on this post-processor against " + registry);
this.registriesPostProcessed.add(registryId);
// 處理配置類
processConfigBeanDefinitions(registry);
}
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// configCandidates為待解析的Configuration類,如配置了@SpringBootApplication的類
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 開始解析
parser.parse(candidates);
parser.validate();
...
while (!candidates.isEmpty());通過源碼可知,具體的解析操作是在org.springframework.context.annotation.ConfigurationClassParser類中
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 將配置類進(jìn)行解析。以當(dāng)前配置類為原配置類,解析@PropertySource、@ComponentScan、@Import、@ImportResource、
// @Bean等標(biāo)注的類或方法,生成對應(yīng)的
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());
}
...
}
// 解析通過@Import引入的配置類,自動配置類的解析也在于此
this.deferredImportSelectorHandler.process();
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// grouping.getImports()方法獲取到了所有配置的可用自動配置類,然后遍歷,以配置類原點又開始一輪解析。自動裝配就是在此處
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// import的解析
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
});通過DeferredImportSelectorGrouping.getImports()方法解析。在org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法中開始了autoConfiguration的解析。
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 解析@EnableAutoConfiguration注解中的屬性exclude、excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 使用SpringFactoriesLoader獲取META-INF/spring.properties中配置的EnableAutoConfiguration實現(xiàn)類,獲取所有配置的自動裝配類
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 獲取需要排除的自動裝配類
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//getConfigurationClassFilter()方法就是獲取spring.factories中配置的AutoConfigurationImportFilter實現(xiàn)類。然后調(diào)用filter //法對自動裝配類進(jìn)行有效性校驗
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}再繼續(xù)看org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.ConfigurationClassFilter#filter
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
// autoConfigurationMetadata為通過META-INF/spring-autoconfigure-metadata.properties配置文件的內(nèi)容
// 使用filter及autoConfigurationMetadata對candidates進(jìn)行校驗
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
...
return result;
}再繼續(xù)看match方法,org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match

@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
// 抽象方法,不同的filter進(jìn)行不同的處理。這里會獲取每一個自動裝配類的條件判斷情況
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}通過match方法,經(jīng)過多種filter的過濾,返回的就是每一個自動配置類是否可用
結(jié)論
SpringBoot項目有一個子項目org.springframework.boot:spring-boot-autoconfigure:xx,這個子項目主要就是做自動裝配的。SpringBoot提前配置了眾多已經(jīng)實現(xiàn)自動配置功能的配置類(org.springframework.boot.autoconfigure.EnableAutoConfiguration接口的實現(xiàn)類)。當(dāng)容器啟動的時候,通過SpringFactoriesLoader將配置類加載進(jìn)容器中- 啟動中,容器通過
BeanFactoryPostProcessor接口解析、修改對象的定義。有一個很重要的配置解析實現(xiàn)類org.springframework.context.annotation.ConfigurationClassPostProcessor,用來解析項目中標(biāo)注@Configuration的類 - 在進(jìn)行配置類解析時(即解析配置了
@SpringBootApplication注解的類),需要經(jīng)過解析類的@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean、接口默認(rèn)實現(xiàn)、父類等(org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass)。對于自動裝配來說,最重要的就是解析@Import - 通過
@Import引入了org.springframework.boot.autoconfigure.AutoConfigurationImportSelector,在進(jìn)行解析@Import引入的配置類時,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry獲取到所有配置的自動裝配類(通過META-INF/spring.factories文件配置EnableAutoConfiguration實現(xiàn)類),通過org.springframework.context.annotation.Condition定義過濾器,判斷自動裝配置是否需要自動裝配。默認(rèn)的過濾器有OnClassCondition、OnWebApplicationCondition、OnBeanCondition,對應(yīng)常見的condition注解ConditionalOnClass、ConditionalOnBean、@ConditionalOnWebApplication。 - 通過過濾判斷,將需要自動配置的類進(jìn)行configuration解析,從而將需要配置的類轉(zhuǎn)換成對應(yīng)的
BeanDefinition進(jìn)行注冊
備注
- SpringBoot將自動裝配類及過濾條件通過配置文件的形式放在了
META-INF目錄下,META-INF/spring.factories和META-INF/spring-autoconfigure-metadata.properties - 在
BeanFactoryPostProcessor進(jìn)行調(diào)用時,有兩種處理。首先是通過BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry解析更多的BeanDefinition,在這里就包含了所有標(biāo)注類的掃描解析,自動裝配類的解析,自動裝配類引入類的解析。在進(jìn)行BeanFactoryPostProcessor#postProcessBeanFactory調(diào)用,進(jìn)行CGLIB-enhanced配置類。這里最重要的一個類就是org.springframework.context.annotation.ConfigurationClassPostProcessor,以下為此類的繼承關(guān)系

到此這篇關(guān)于SpringBoot是如何做到自動裝配的的文章就介紹到這了,更多相關(guān)SpringBoot自動裝配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
最新hadoop安裝教程及hadoop的命令使用(親測可用)
這篇文章主要介紹了最新hadoop安裝教程(親測可用),本文主要講解了如何安裝hadoop、使用hadoop的命令及遇到的問題解決,需要的朋友可以參考下2022-06-06
SpringCloudGateway使用Skywalking時日志打印traceId解析
這篇文章主要為大家介紹了SpringCloudGateway使用Skywalking時日志打印traceId解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Java編程中應(yīng)用的GUI設(shè)計基礎(chǔ)
這篇文章主要介紹了Java編程中應(yīng)用的GUI設(shè)計基礎(chǔ),為一些Java開發(fā)CS類型應(yīng)用的基礎(chǔ)概念知識,需要的朋友可以參考下2015-10-10
Java常用數(shù)字工具類 數(shù)字轉(zhuǎn)漢字(1)
這篇文章主要為大家詳細(xì)介紹了Java常用數(shù)字工具類,數(shù)字轉(zhuǎn)漢字,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05

