Spring源码阅读 - @Import一个继承 ImportBeanDefinitionRegistrar 的类
1. ImportBeanDefinitionRegistrar
// 和 DeferredImportSelectors 类似, 也可以自己注册 BeanDefinition
// 不过这个接口可以继承 EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware, 或者提供参数为 Environment、... 的构造函数\
// 不过也只是经过了特殊的的初始化, 和普通 Bean 通过 getBean 经过完整生命周期还是不一样的
public interface ImportBeanDefinitionRegistrar {
// 注册BD, 默认行为啥也不做
// 不建议在这里注入 BeanDefinitionRegistryPostProcessor, 因为处理到他的时候, 配置类的处理流程已经完毕【??待续后续代码】
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
2. 处理流程
org.springframework.context.annotation.ConfigurationClassParser#processImports
// Import 的类继承了 ImportSelector 接口
if (candidate.isAssignable(ImportSelector.class)) {
// ...
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// ...
}
// 继承了 ImportBeanDefinitionRegistrar 接口,说明这个类有想自己向容器注入 BD 的想法,比如说 MyBatis 自己收集接口, 自己注入这些接口代理类的 BD
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
// 实例化, 这个调用和上面的调用是一致的, 也就是说 ImportSelector 也可以继承一些 Aware 接口, 或提供构造函数
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 也没有立即处理, 注意这里是放到 configClass 中的
// 而 DeferredImportSelector 是放到了一个集合中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
3. 处理时机
注意上面是将这个 ImportBeanDefinitionRegistrar 绑定在了 configClass 中,从这就可看出它的执行应该是较晚的,实际比 DeferredImportSelector 还晚。
回到 ConfigurationClassPostProcessor#processConfigBeanDefinitions
do {
// 这里是解析配置类, DeferredImportSelector 就是在里面就被调用了
parser.parse(candidates);
parser.validate();
// 这里面应该是 Parser 记录的解析过的配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 去除已经解析过的,这个实际主要是用在后面 loadBeanDefinitions 去除不必要的 BD, removeAll 是为了减少判断
// alreadyParsed 是上一次循环已经解析过的,去除上一次解析过的就是这一次解析过的,主要是 Parser 是复用的,所以需要 removeAll
configClasses.removeAll(alreadyParsed);
// 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());
}
// 这里就处理了 ImportBeanDefinitionRegistrar, 迟于 DeferredImportSelector
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
// ...
}
while (!candidates.isEmpty());
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix