这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」
前言
上一篇文章中我们通过idea引用查找的功能,找到分析了:
- @Import注解是在哪里被处理的
这里我们接着往下,来看看@Import最终具体执行的三种情况:
- DeferImportSelector
- ImportBeanDefinitionRegistrar
- 可以按照**@Configuration**处理的
这些情况下分别是如何处理的。
DeferImportSelector
上一篇文章里我们知道了:
- DeferImportSelector在processImport方法中的处理方式,暂时认为只是暂时保存到deferredImportSelectors这个list中保存起来了。
那么,这些保存起来的信息具体是在哪里使用的?
处理
这个类中有一个process方法,那么就应该是处理的方法了,我们来看看代码:
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
//这里的register其实只是把数据往里面塞的动作
deferredImports.forEach(handler::register);
//看下面
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
//是不是看到了我们的老朋友?
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);
}
});
}
}
复制代码
这里就看到,又调到processImports里了。
之前的handler的方法我们没有细究,这里再贴一下代码,这样子运转机制就清晰了:
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回收掉。
这个关系如下:
而这个容器(deferredImportSelectors),只在process方法的时候会置空,处理完之后就变成了新的容器(new)。
这说明:
- 这里的defer延迟,意即:
- 如果process方法不在执行中时,这些importSelector会暂时保存起来。
- 如果process方法在执行并且已经开始了(标志是:容器为空),那么此时不能偷懒往后延迟了,只能自己来执行。
在DeferredImportSelectorHandler的handle方法上的注解很明确地说了这一点:
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中,这部分的处理其实也比较简单:
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
复制代码
实际上,就是实例化该类,并放到configClass中。那么这部分最终在哪里处理呢?
借助idea可以看到在这里被用到了(类:ConfigurationClassBeanDefinitionReader):
//这个方法在loadBeanDefinitions被循环调用
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//........
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
//一般来说,这里的registry实际上是在Spring启动中的容器(各种context)
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
//而上面的这里:
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//子类实现
}
复制代码
看到registerBeanDefinitions,这里就比较清楚了:
- 解析到的ImportBeanDefinitionRegistrar,最终会被丢到context中来初始化成beanDefinition。
这个接口的注解也说明了这一点:
Register bean definitions as necessary based on the given annotation metadata of
the importing {@code @Configuration} class.
复制代码
如果要真的看明白这个方法是如何实现的,那么就需要trace到上游调用和子类实现了,因此我们放到后面再说。【ImportBeanDefinitionRegistrar】
@Configuration方式
这里的处理在这里:
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//如果这里已经保存了
if (existingClass != null) {
//并且想放进去的configClass,importedBy容器中不是空的
if (configClass.isImported()) {
//原来已有的也是通过@Import注册的(importedBy容器中不是空的)
if (existingClass.isImported()) {
//我们把原来的合并一下
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
//这里的else对应的是上面的configClass.isImported(),意思就是:
//我们期望放进来的,不是通过import注解的
//看下面的注释,意思就是:把原来的记录给删掉,已现在的为准
//但是我们是通过@Import注解进来的,这个是咋会不是import的呢?
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
//递归地构建这个sourceClass
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
//这里是processImport调用的一个地方,在前面我们提到过,到这里就变成递归调用了
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
复制代码
这里也能看到:
- 如果是@Configuration,其实最终还是得变成ImportBeanDefinitionRegistrar,否则就继续递归了。
小结
看完这部分,应该对processImport中的四种方式有了一个大概了解:
-
如果是要最终解析的,那么八成是得通过ImportBeanDefinitionRegistrar来导入了,否则就是一直递归到最下面,变成ImportBeanDefinitionRegistrar。
-
这里埋得坑:
- 类:【ImportBeanDefinitionRegistrar】
- 何处处理的?
- 子类如何实现的?
- configurationClass类中,loadBeanDefinitionsForConfigurationClass在哪里,何时被调用?
- 方法:processConfigurationClass
- 这个方法中为什么会有ConfigurationClass不是import的情况出现呢,也就是说:
- ConfigurationClass中的importedBy是如何维护的?
- 这个方法中为什么会有ConfigurationClass不是import的情况出现呢,也就是说:
- 还有之前没有处理的问题:
- doProcessConfigurationClass干了啥?
- 类:【ImportBeanDefinitionRegistrar】
近期评论