前言
在正式开始之前,不妨先思考几个关键的问题:
- SpringBoot 如何确定哪些类是需要自动装配的?
- 自动装配类大量用到了 @Condition 注解,那如何保证所有用户自定义的类先于自动配置类加载呢?
- 如果自动装配的类需要知道用户代码的路径,该如何做呢?
- @AutoConfigurationPackage 的作用和意义是什么?
带着这些问题,就开始读源码了。
本文基于 SpringBoot 2.7.2
@SpringBootApplication 注解
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
可以看到 @SpringBootApplication 有三个注解组合而成。
1、@ComponentScan:包扫描路径
@ComponentScan 是 Spring 的注解,用于扫描带有 @Component 等注解的类并注入到 Spring 容器中。
2、@SpringBootConfiguration:配置类
java
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
我们发现 @SpringBootConfiguration 和 @Configuration 几乎没有差别。
javadoc 是这样说的:
Indicates that a class provides Spring Boot application @Configuration. Can be used as an alternative to the Spring's standard @Configuration annotation so that configuration can be found automatically (for example in tests). Application should only ever include one @SpringBootConfiguration and most idiomatic Spring Boot applications will inherit it from @SpringBootApplication.
大概就是,@SpringBootConfiguration 是专门为 SpringBoot 启动类定制的 @Configuration 注解,且每个 Spring Boot applications 只能有一个类有 @SpringBootConfiguration,我理解就是与普通的 @Configuration 做一个区分。
3、@EnableAutoConfiguration:开启自动配置功能
@EnableAutoConfiguration 是实现自动配置的核心,重点看:
java
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 排除自动配置类
Class<?>[] exclude() default {};
// for example
// 移除 Auto-configuration for Spring Data's Redis support.
// @SpringBootApplication(exclude = {RedisAutoConfiguration.class})
String[] excludeName() default {};
}
这又是个复合注解,@AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)。
@Import 的作用,根据类实现不同接口会有所不同,后面具体情况具体分析
@AutoConfigurationPackage:缓存包路径
先说结论:@AutoConfigurationPackage 会暂存包路径,即 @AutoConfigurationPackage 修饰的类所在的包。
@AutoConfigurationPackage 源码如下:
java
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
在默认情况下,basePackage 为该注解修饰的类的包。
另外,@AutoConfigurationPackage 引入了 AutoConfigurationPackages.Registrar
:
java
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
// 注意 register 方法的第二个参数,这实际上就是你的启动类 MainApplication 所在的包路径。
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
因为 Registrar 实现了 ImportBeanDefinitionRegistrar ,因此 Spring 会自动调用 registerBeanDefinitions 方法。这部分的源码如下:
java
if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 反射 加载 class 对象
Class<?> candidateClass = candidate.loadClass();
// 创建 registrar 实例对象 并回调 Aware 接口
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// addImportBeanDefinitionRegistrar
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
ConfigurationClass 有这么一个集合 LinkedHashMap:
java
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
addImportBeanDefinitionRegistrar 就是把这个 registrar 放入这个 map 中。
registrar 处理时机
而真正调用 registrar 是在 loadBeanDefinitions 时:
方法是这样的:
java
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// ...
// 处理 @Bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 处理 ImportedResources
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 处理 Registrars
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
我们关注 loadBeanDefinitionsFromRegistrars 方法
java
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
回调了 Registrar 的 registerBeanDefinitions 方法
java
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
这里的 BEAN 是 org.springframework.boot.autoconfigure.AutoConfigurationPackages,执行 registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames)); 方法
java
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
最终向 BeanDefinationMap 注入了 AutoConfigurationPackages,唯一的作用是保存 basePackages 属性。
那费劲心思注入这么个 BeanDefination 干啥呢?源码上的注释是这样的:
Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner)
即保存 auto-configuration packages,使得之后可能需要获得。有些 jar 包可能需要获取用户代码来执行某些操作,于是就需要依赖这个 BeanDefination。
一处使用是 JacksonAutoConfiguration:
java
@Bean
public JsonMixinModule jsonMixinModule(ApplicationContext context) {
List<String> packages = AutoConfigurationPackages.has(context) ? AutoConfigurationPackages.get(context)
: Collections.emptyList();
return new JsonMixinModule(context, packages);
}
为什么要借助 Registrar
ImportBeanDefinitionRegistrar 的 javadoc 是这样写的:
Interface to be implemented by types that register additional bean definitions when processing @Configuration classes.
Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.
强调了这个接口在 bean definition level 进行操作时非常有用。
如何理解这个 bean definition level
呢?
像 @Bean 注解,我们关注的重点在方法调用后生成的实例,但 ImportBeanDefinitionRegistrar 不同,它关注的是 BeanDefination 而非实例对象,或者说实例对象根本不重要。
因此,Spring 只是使用了 AutoConfigurationPackages 来保存 auto-configuration packages,仅此而已。
AutoConfigurationImportSelector:导入自动配置类
现在我们回到 @EnableAutoConfiguration 的源码,还用 @Import 注入了 AutoConfigurationImportSelector
java
@Import(AutoConfigurationImportSelector.class)
我们发现 AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口。
而 DeferredImportSelector 的 Javadoc 是这么说的:
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
即在处理 AutoConfigurationImportSelector 时,所有其它 @Configuration 已经 process 完毕。
关于 DeferredImportSelector 如何保证在处理时其它 @Configuration 已经 process 完毕,我放在文章最后的附录部分。
并且强调了搭配 @Conditional 注解 particularly useful。我们知道,@Conditional 注解是根据条件来判断是否加载 Bean 的,比如 @ConditionalOnExpression,只有当表达式为 true 的时候才会加载 Bean。所以 DeferredImportSelector + @Conditional 就是实现按需自动配置的关键。
下面我们直接看处理 AutoConfigurationImportSelector 的方法:
java
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// getImports
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
// processImports 处理 @Import
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),exclusionFilter, false);
});
}
}
这里又分为两个部分:
- getImports,获取自动配置类
- processImports,处理自动配置类
获取自动配置类:getImports
getImports 长这样:
java
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 调用 group.process
// 两个参数 启动类 MainApplication 和 AutoConfigurationImportSelector
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 调用 group.selectImports
return this.group.selectImports();
}
因为 AutoConfigurationImportSelector 内部类 AutoConfigurationGroup 实现了 DeferredImportSelector.Group,因此有别于默认的情况,group.process 长这样:
java
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// getAutoConfigurationEntry
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
可以看到这里就拿到了 AutoConfigurationEntry,并且包含了自动配置类:
而 getAutoConfigurationEntry 方法就会返回所有应该被导入的自动配置类 auto-configurations。
java
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 拿到 configurations 集合
List<String> configurations =
getCandidateConfigurations(annotationMetadata, attributes);
// 省略 过滤 事件传播机制等
return new AutoConfigurationEntry(configurations, exclusions);
}
通过 getCandidateConfigurations 方法获取 configurations 集合并进行过滤,继续看 getCandidateConfigurations 方法:
java
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
// 1. loadFactoryNames
// 处理 META-INF/spring.factories
// 第一个参数 EnableAutoConfiguration.class
// 第二个参数 类加载器
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()));
// 2. load
// 处理META-INF/spring/.../.AutoConfiguration.imports
ImportCandidates.load(AutoConfiguration.class,
getBeanClassLoader()).forEach(configurations::add);
// configurations 为空的情况
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
我特地保留了 Assert ,透露了很多关键信息。可以看到 auto configuration classes 是分为两类的:
- META-INF/spring.factories
- META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
实际上,loadFactoryNames 专门拿到 META-INF/spring.factories 的自动配置类,而 load 方法专门处理 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
的自动配置类。
老版本的 SpringBoot 可能只有 META-INF/spring.factories,即没有这个 load 方法
1、获取 META-INF/spring.factories
spring.factories 文件大概是下面这样:
ini
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lun.hello.spring.boot.autoconfigure.HelloAutoConfiguration
# mybatis-spring-boot-autoconfigure:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
执行完 SpringFactoriesLoader.loadFactoryNames 后,拿到了所有引入的 jar 的自动配置类,所以我们重点关注 loadFactoryNames 方法。
但奇怪的是,loadSpringFactories 命中了缓存并直接返回了。
SpringFactoriesLoader 的这个 cache 是一个 ConcurrentReferenceHashMap。
java
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
我们发现,在 SpringApplication 初始化时,就会调用这个方法:
java
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// loadFactoryNames 方法
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 反射 实例化
// 但是返回值为 null 因此在这不会实例化这些类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
此时再看 loadSpringFactories 方法:
java
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 省略 cache
result = new HashMap<>();
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
// APP ClassLoader 负责加载我们自定义的 bean 和 jar 包
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
// 遍历 url
URL url = urls.nextElement();
// 每个 url 可以理解成一个 META-INF/spring.factories 文件
UrlResource resource = new UrlResource(url);
// 获取文件中的 k-v 键值对
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// key
String factoryTypeName = ((String) entry.getKey()).trim();
// value value 可以有多个
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
// 遍历每个 value
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
// 去重,不可修改
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
// 放入缓存
cache.put(classLoader, result);
return result;
}
整个 result(cache) 大概长这样:
我们来看一个例子:
META-INF/spring.factories 长这样:
ini
org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory
最终对应到这样的结果:
也是因此,后面调用 loadSpringFactories 方法都是直接从 cache 里返回:
java
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
// ...
}
但是会根据 factoryType 做过滤,比如这样调用:
java
List<String> autoConfigurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader));
// 只会返回 xxxAutoConfiguration 自动配置类
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
只会返回 key 为 EnableAutoConfiguration 的类。
总之,通过此处 SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()));
我们拿到了所有 META-INF/spring.factories 中的自动配置类。
2、获取 AutoConfiguration.imports
再看 load 方法:
java
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
// annotation 就是 AutoConfiguration.class
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// 拼接 location 即:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
String location = String.format(LOCATION, annotation.getName());
// 找资源路径
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> autoConfigurations = new ArrayList<>();
// 读取 xxx.AutoConfiguration.imports 文件并加入 autoConfigurations 集合
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
autoConfigurations.addAll(readAutoConfigurations(url));
}
return new ImportCandidates(autoConfigurations);
}
url 大概长这样:
jar:file:/{本地Maven仓库路径}/org/springframework/boot/spring-boot-autoconfigure/2.7.2/spring-boot-autoconfigure-2.7.2.jar!/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
然后依次读取每一行并放入 autoConfigurations 集合中。
3、收尾工作
最后,将两个方法的结果合并,就是 getAutoConfigurations 方法的返回值,当然 getAutoConfigurationEntry 会进行一些过滤:
java
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
此时回到 getImports 方法的最后,会调用 group.selectImports,处理一些 Exclusions 和排序规则。
java
public Iterable<Entry> selectImports() {
// 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.removeAll(allExclusions);
// sort and return
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
至此,整个 getImports 方法分析完毕。
回顾一下,getImports 以后,我们对每个 Entry 调用 processImports:
java
// getImports
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
// processImports 处理 @Import
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),exclusionFilter, false);
});
处理自动配置类:processImports
这个 processImports 方法,就是处理 @Import 注解的方法,在解析 @Configuration 时就出现过,简单回忆一下:
java
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter){
// ...
// 3. Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 5. Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
return null;
}
而自动配置类通常没有实现 import 的接口,因此一般走下面这段逻辑:
java
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// processConfigurationClass
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
而 processConfigurationClass 就是常规解析 @Configuration 的流程了:
java
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
那么随着自动配置类被 process ,整个自动装配原理也就讲的差不多了。
附录:DeferredImportSelector 如何保证最后处理
@Configuration 的类会用 doProcessConfigurationClass 方法进行处理:
java
// ...
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// ...
// @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
对 @Import 核心的处理如下:
java
for (SourceClass candidate : importCandidates) {
// 因为 DeferredImportSelector extends ImportSelector
// 所以实现了 DeferredImportSelector 的类这个 if 也命中
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 处理 DeferredImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
Deferred 延迟的关键
我们发现执行完这个方法后,@Import 的构造方法是被调用了,但 DeferredImportSelector 的 selectImports 方法还没有被调用。
java
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
而实现延迟处理的关键是:
java
this.deferredImportSelectors.add(holder);
会将 DeferredImportSelector 封装成 DeferredImportSelectorHolder,并放入一个 ArrayList 中。
java
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
而回调的时机在 parse 方法的最后:
java
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
// parse ...
}
this.deferredImportSelectorHandler.process();
}
但你会说,处理 @Configuration 的 @ComponentScan 时会递归调用 parse ,这个 process 的时机,是不是每个 @Configuration 类解析完就处理呢?但源码注释明明是所有 @Configuration 。
java
// 递归调用 parse 的源码
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
而递归调用的 parse 是这样的,入参不同,最后也不会有 this.deferredImportSelectorHandler.process()
java
protected final void parse(@Nullable String className, String beanName){
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(Class<?> clazz, String beanName){
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(AnnotationMetadata metadata, String beanName){
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
所以可以保证 process 延迟处理在最后,这就和注释中提到的 runs after all @Configuration beans have been processed
一致了。