这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」
前言
上一篇文章中我们通过idea引用查找的功能,找到分析了:
- @Import注解是在哪里被处理的
这里我们接着往下,来看看@Import最终具体执行的三种情况:
- DeferImportSelector
- ImportBeanDefinitionRegistrar
- 可以按照**@Configuration**处理的
这些情况下分别是如何处理的。
DeferImportSelector
上一篇文章里我们知道了:
- DeferImportSelector在processImport方法中的处理方式,暂时认为只是暂时保存到deferredImportSelectors这个list中保存起来了。
那么,这些保存起来的信息具体是在哪里使用的?
处理
这个类中有一个process方法,那么就应该是处理的方法了,我们来看看代码:
1 | java复制代码public void process() { |
这里就看到,又调到processImports里了。
之前的handler的方法我们没有细究,这里再贴一下代码,这样子运转机制就清晰了:
1
2
3
4
5
6
7
8
9
10
11
12
13 > java复制代码public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
> DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
> if (this.deferredImportSelectors == null) {
> DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
> handler.register(holder);
> handler.processGroupImports();
> }
> else {
> this.deferredImportSelectors.add(holder);
> }
> }
>
>
其实这里的processGroupImport,在处理import注解的Defer的时候已经调用了(processImport),那么此处综合起来看就是
- 处理的时候,每当有一个新的Defer进来,先处理之前的存的,再把这个defer的存起来。
- 最后调到这个process的时候,就会把所有的group执行一下,并把这些收集的group回收掉。
这个关系如下:
processImport新的defer来了如果容器为空注册holder并执行如果容器不为空存起来process将容器置为null调用processGroupImport注册所有holder并执行新建容器
而这个容器(deferredImportSelectors),只在process方法的时候会置空,处理完之后就变成了新的容器(new)。
这说明:
- 这里的defer延迟,意即:
- 如果process方法不在执行中时,这些importSelector会暂时保存起来。
- 如果process方法在执行并且已经开始了(标志是:容器为空),那么此时不能偷懒往后延迟了,只能自己来执行。
在DeferredImportSelectorHandler的handle方法上的注解很明确地说了这一点:
1
2
3
4
5
6 > java复制代码Handle the specified {@link DeferredImportSelector}. If deferred import
> selectors are being collected, this registers this instance to the list. If
> they are being processed, the {@link DeferredImportSelector} is also processed
> immediately according to its {@link DeferredImportSelector.Group}.
>
>
调用
代码中我们可以发现:
- 只有通过handler,才能调用和保存这些DeferredImportSelector。
而这个类只在ConfigurationClassParser.parse和processImports中被调用:
- parse调用process方法
- processimport调用handle方法
小结
这里也能看出来,DeferredImportSelector也并没有实际上将import落地处理掉,处理的时候也是得委托到下面两个来。
ImportBeanDefinitionRegistrar
在processImport中,这部分的处理其实也比较简单:
1 | java复制代码Class<?> candidateClass = candidate.loadClass(); |
实际上,就是实例化该类,并放到configClass中。那么这部分最终在哪里处理呢?
借助idea可以看到在这里被用到了(类:ConfigurationClassBeanDefinitionReader):
1 | java复制代码//这个方法在loadBeanDefinitions被循环调用 |
看到registerBeanDefinitions,这里就比较清楚了:
- 解析到的ImportBeanDefinitionRegistrar,最终会被丢到context中来初始化成beanDefinition。
这个接口的注解也说明了这一点:
1 | java复制代码Register bean definitions as necessary based on the given annotation metadata of |
如果要真的看明白这个方法是如何实现的,那么就需要trace到上游调用和子类实现了,因此我们放到后面再说。【ImportBeanDefinitionRegistrar】
@Configuration方式
这里的处理在这里:
1 | java复制代码processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); |
这里也能看到:
- 如果是@Configuration,其实最终还是得变成ImportBeanDefinitionRegistrar,否则就继续递归了。
小结
看完这部分,应该对processImport中的四种方式有了一个大概了解:
- 如果是要最终解析的,那么八成是得通过ImportBeanDefinitionRegistrar来导入了,否则就是一直递归到最下面,变成ImportBeanDefinitionRegistrar。
- 这里埋得坑:
+ 类:【**ImportBeanDefinitionRegistrar**】
- 何处处理的?
- 子类如何实现的?
- configurationClass类中,**loadBeanDefinitionsForConfigurationClass**在哪里,何时被调用?
+ 方法:**processConfigurationClass**
- 这个方法中为什么会有**ConfigurationClass**不是import的情况出现呢,也就是说:
* ConfigurationClass中的importedBy是如何维护的?
+ 还有之前没有处理的问题:
- **doProcessConfigurationClass**干了啥?
本文转载自: 掘金