目錄
一、前言
二、SpringBoot自動裝配原理
2.1、@SpringBootApplication注解
2.2、AutoConfigurationImportSelector
一、前言
上一篇文章,通過分析refresh()方法中的invokeBeanFactoryPostProcessors()方法,分析了IoC容器的初始化過程,這一節從代碼上如下所示,接上一節ConfigurationClassParser類中的parse()方法,接著分析SpringBoot的自動裝配原理。
// ConfigurationClassParser類
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 如果是SpringBoot項目進來的,bd其實就是前面主類封裝成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的實現類)
if (bd instanceof AnnotatedBeanDefinition) {
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);
}
}
// 加載默認的配置---》(對springboot項目來說這里就是自動裝配的入口了)
processDeferredImportSelectors();
}
二、SpringBoot自動裝配原理。
2.1、@SpringBootApplication注解
對這個注解詳細大家一定非常熟悉了。再來好好看看這個注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
接著看@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
OK,看到@Import(AutoConfigurationImportSelector.class)導入了一個重要的類AutoConfigurationImportSelector。
2.2、AutoConfigurationImportSelector
// AutoConfigurationImportSelector類
//自動裝配
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//獲取所有的自動配置類(META-INF/spring.factories中配置的key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的類)
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
//需要排除的自動裝配類(springboot的主類上 @SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})指定的排除的自動裝配類)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//將需要排除的類從 configurations remove掉
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
至于怎么從章節一中提到的ConfigurationClassParser類中的parse()===>processDeferredImportSelectors()==>AutoConfigurationImportSelector#selectImports(),篇幅有限不做過多介紹。
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
我們來看一下getCandidateConfigurations()方法是怎么拿到這些自動配置類的。
// AutoConfigurationImportSelector類
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
是不是又看到一個十分熟悉的方法loadFactoryNames(),沒錯,其實我們在分析SpringBoot啟動流程的第一篇文章的時候,就已經分析了,SpringBoot是如何從META-INF/spring.factories中加載指定key的value的。ok,我們在這里再次回顧一遍。
看看loadFactoryNames()方法
// SpringFactoriesLoader類
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
debug,看看要從META-INF/spring.factories中加載的類的key,如下圖所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration
回到selectImports()方法,debug,跳過List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations
竟然有110個,那這些類都在哪里呢?看spring-boot-autoconfigure(當然在SpringBoot的工程中,也不止這一個依賴包中存在該配置文件)工程下的META-INF/spring.factories,我們能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定義了一大堆。
其中還有一個com.demo.starter.config.DemoConfig是我自定義的starter。如下所示,我在測試工程中添加了自定義starter的依賴,所以SpringBoot就能掃描到。
<dependency>
<groupId>com.demo</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-RELEASE</version>
</dependency>
繼續看Set<String> exclusions = getExclusions(annotationMetadata, attributes);方法,該方法是排除主類上@SpringBootApplication注解上排除的自動裝配的類。比如我們在該注解上排除我們自定義starter的自動裝配的類,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(當然也可以用excludeName進行排除),那么在后面的configurations.removeAll(exclusions);方法中將會刪除我們的com.demo.starter.config.DemoConfig.class。
configurations = filter(configurations, autoConfigurationMetadata);該行代碼將會過濾掉不需要裝配的類。過濾的邏輯有很多,比如我們常用的@ConditionXXX注解。如下所示:
@ConditionalOnBean:容器中有指定的Bean
@ConditionalOnClass:當類路徑下有指定的類
@ConditionalOnExpression:基于SpEL表達式作為判斷條件
@ConditionalOnJava:基于JVM版本作為判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查找指定的位置
@ConditionalOnMissingBean:當容器中沒有指定Bean的情況下
@ConditionalOnMissingClass:當類路徑下沒有指定的類
@ConditionalOnNotWebApplication:當前項目不是Web項目
@ConditionalOnProperty:配置文件中指定的屬性是否有指定的值
@ConditionalOnResource:類路徑下是否有指定的資源
@ConditionalOnSingleCandidate:當指定Bean在容器中只有一個,或者雖然有多個但是指定首選Bean
@ConditionalOnWebApplication:當前項目是Web項目的條件下
至于如何將這些類解析成BeanDefinition并注冊進beanDefinition中的,和上一節講的過程是一樣的,不再贅述了。
debug,跳過refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);方法。如下圖所示,最終在beanFactory的BeanDefinitionMap中找到了自定義starter中的自動裝配的類。
綜合本文和上一篇博文我們詳細的梳理了IoC容器的初始化過程,到此IoC容器的初始化過程就結束了。