@Import注解的源码分析

缘起

@Import注解大家都用过,是Spring提供给大家用于导入组件用的,那么它的原理是什么呢? 且听我慢慢道来.

分析

debug用的demo见【1】.

我想说,一切还是源于【1】中的那个关键的BeanDefinitionRegistryPostProcessor接口的实现类——ConfigurationClassPostProcessor. 它的注册外加实例化是在【1】中说过了(搜索”清楚顺序了吗”).

我们关心的是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry接口的工作(就是引入bean的定义).

首先看一下调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
-->
org.springframework.context.support.AbstractApplicationContext.refresh()
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
-->org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
-->
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List<BeanFactoryPostProcessor>)
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
-->
org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor>, BeanDefinitionRegistry)
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry); // 这里的postProcessor就是ConfigurationClassPostProcessor,调用它作为BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry接口
}
-->
org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)
processConfigBeanDefinitions(registry);
-->
org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry)
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames(); // 因为经过了AnnotationConfigApplicationContext(Class<?>...)这个构造器中的this() 和 register(annotatedClasses),所以candidateNames中已经有了6个bean——包括mainConfig以及ConfigurationClassPostProcessor

for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
} // 这里只会加入mainConfig(我们的配置类),其余的beanName都被过滤掉了
...
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
parser.parse(candidates); // parser就是ConfigurationClassParser,后续的调用栈就是从这里进入的
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
...
// 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());
}
this.reader.loadBeanDefinitions(configClasses);
-->
org.springframework.context.annotation.ConfigurationClassParser.parse(Set<BeanDefinitionHolder>)
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
-->
org.springframework.context.annotation.ConfigurationClassParser.parse(AnnotationMetadata, String)
processConfigurationClass(new ConfigurationClass(metadata, beanName));

​ 源码1

跟进57行(这是我们的重头戏了)

org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClass)

1
2
3
4
5
6
7
8
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);

​ 源码2

其中源码2的configClass就是mainConfig. 我们先不说别的,就说源码2的第8行,将configClass加入configurationClasses中去意图是什么? 这就不得不回到源码1的第43行到第51行. 所以我们就知道了源码2 中的configurationClasses就是源码1的第43行从parser中取出来,形成configClasses,然后第源码1的第51行去加载configurationClasses中bean的定义. 这里先打住,即先打住在

源码1的第51行去加载configurationClasses中bean的定义

我们看看configurationClasses是怎么从源码2诞生的. 跟进源码2 的第四行,

org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass, SourceClass)

1
2
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

​ 源码3

注释写的都很明显了——处理 @Import 注解的方法. 此时源码3的configClass还是 mainConfig. 而sourceClass 也是MainConfig. getImports(sourceClass)得到[com.yfs.bean.Color, com.yfs.bean.Human], 即MainConfig上的@Import 注解引入的两个bean. getImports 的源码如下

org.springframework.context.annotation.ConfigurationClassParser.getImports(SourceClass)

1
2
3
4
5
6
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}

​ 源码4

collectImports源码如下

org.springframework.context.annotation.ConfigurationClassParser.collectImports(SourceClass, Set, Set)

1
2
3
4
5
6
7
8
9
10
11
12
13
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {

if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited); // 递归
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}

​ 源码5

其实不难想象collectImports显然是要用到递归的. 知道这一点就行了,我们就不具体分析源码5了.

然后我们跟进源码3 的processImports方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

if (importCandidates.isEmpty()) { // 此时importCandidates就是源码3的getImports得到的结果, 即Color和Human两个bean
return;
}
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 调用ImportSelector接口的selectImports方法得到要引入的类的全限定名
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, 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 =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); // 往ConfigurationClass的importBeanDefinitionRegistrars中添加此registrar
}
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)); // 递归
}
}
}

​ 源码6

这里就能看到了Spring框架对于ImportSelector接口、ImportBeanDefinitionRegistrar接口的处理. 如果熟悉这两个接口的使用的读者的话,就不难看出源码6对于第19行对ImportSelector接口的处理以及源码32对ImportBeanDefinitionRegistrar的处理了(关于对ImportBeanDefinitionRegistrar的处理这里和之前一样,也是停一下,打住).

如果既没有实现ImportBeanDefinitionRegistrar接口也没有实现 ImportSelector接口的话,就走源码6的第39行——递归调用processConfigurationClass. 即源码 1 的最后一行. 注意,因为在DEMO【1】 中,candidate就是Color和Human两个普通的类,所以并没有实现这两个接口中的任何一个. 所以走的就是这里. 所以我们跟进源码6的最后一行,其中candidate就是Color或者Human其中之一.

然后又来到了源码2(因为源码2就是processConfigurationClass方法的源码) . 此时源码2的configClass是Color. 然后来到了源码3 ,进而来到源码6,但是此时和源码6的第四行的importCandidates是源码3 的getImports(sourceClass)得到的,其中sourceClass是com.yfs.bean.Color, 当然是空数组啦~ 因为说了com.yfs.bean.Color仅仅是一个普通的类——上面没有任何@Import注解. 所以源码6的第5行就返回了.同理对于Human也是这样. 然后源码2就是简单把com.yfs.bean.Color加入ConfigurationClassParser的configurationClasses了事.

综上,我们分析了 @Import 引入了的普通类,ImportBeanDefinitionRegistrar接口实现类、ImportSelector实现类是怎么处理的——即怎么加入到源码1的parser.getConfigurationClasses()中去的. 但是这里有一个例外,就是ImportBeanDefinitionRegistrar的接口实现类,你看看源码6的32行就知道它并没有进行任何递归,而是加入到了一个ConfigurationClass的属性importBeanDefinitionRegistrars中去,至于有什么用,我们马上就知道了.

现在我们可以回到源码1的第51行——正式加载bean定义了(传入的configClasses就是[ConfigurationClass: beanName ‘null’, com.yfs.bean.Color, ConfigurationClass: beanName ‘null’, com.yfs.bean.Human, ConfigurationClass: beanName ‘mainConfig’, com.yfs.config.MainConfig]这三个上面分析加入的). 跟进去

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(Set)

1
2
3
4
5
6
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) { // Human、Color、MainConfig
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}

​ 源码7

可见,这里就是拿上面获取的3个configClass进行遍历,跟进源码7的第4行

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClass, TrackedConditionEvaluator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {

if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}

if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

​ 源码8

其实这段代码在【1】中的源码3中我们分析 @Configuration已经见过了. 只是那里关注的是@Configuration+@Bean的组合,即关注的是源码8的第17行,而这里关注第14行和第20行。

源码8第13行的configClass的取值有Color、Human、MainConfig三个(因为源码7进行的遍历嘛~).

对于前两者,他们是被MainConfig引入的,所以源码8的第13行返回的是true,就会执行源码8的第14行. 我们跟进去

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass)

1
2
3
...
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
...

​ 源码9

可见,完成了Color和Human的bean定义的注册(通过beanFactory,即ioc容器).

然后Color和Human没有 @Bean注解的方法,所以源码8的16~18不会执行.

注意,源码6的第32行加入的ImportBeanDefinitionRegistrar接口就会在源码8的第20行被调用. 调用的形式是什么呢?

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata>)

1
2
3
4
5
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) {
entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
}
}

​ 源码10

其实就是调用ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法. 而我们自己实现的这个方法往往就是使用该方法的第二个入参——ioc容器直接调用ioc容器的registerBeanDefinition方法注册bean定义而已.

好了,@Import 注解的源码分析已经分析完了.

总结一下,还是ConfigurationClassPostProcessor,它在AnnotationConfigApplicationContext的构造器方法的this()中注册并实例化之后,在AnnotationConfigApplicationContext构造器方法中的refresh中的invokeBeanFactoryPostProcessors调用中对ConfigurationClassPostProcessor作为BeanDefinitionRegistryPostProcessor接口的实现类的postProcessBeanDefinitionRegistry方法的调用中,然后就是ConfigurationClassPostProcessor的processConfigBeanDefinitions方法的调用,这个方法里面会先解析 @Import注解,然后得到一堆的configClasses,最后

1
this.reader.loadBeanDefinitions(configClasses);

注册他们的bean定义了事~

参考

【1】https://yfsyfs.github.io/2019/06/23/Configuration-%E6%B3%A8%E8%A7%A3%E6%BA%90%E7%A0%81/

DEMO

【1】https://github.com/yfsyfs/backend/tree/master/spring-annotation-import