Spring源码阅读 - @Import 一个普通类
1. 解析逻辑
具体逻辑在 org.springframework.context.annotation.ConfigurationClassParser#processImports
中,下面的 importCandidates 即为 @Import 导入的类的列表。
最后的一个 else 即为普通类的处理逻辑,实际上就是将其当做一个配置类进行处理。
同时要注意到,在这里为止,@Import 导入的类还没有变为 BD 被注入容器,那么这个时机在哪?
for (SourceClass candidate : importCandidates) {
// Import 的类继承了 ImportSelector 接口
if (candidate.isAssignable(ImportSelector.class)) {
// ...
}
// 继承了 ImportBeanDefinitionRegistrar 接口,说明这个类有想自己向容器注入 BD 的想法,比如说 MyBatis 自己收集接口, 自己注入这些接口代理类的 BD
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// ........
}
else {
// 普通类,这里的普通类包括 @Import 直接导入的没有继承上面三个接口的类,还有就是 @Import 导入了继承 ImportSelector 接口的类要注入的普通类
// 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);
}
}
2. 注入时机
从上面可见,被 @Import 的这些普通类并未被直接转换 BD 注入容器,仅是将这些类当做配置类处理一遍。
调用的处理配置类的方法为:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// ... 略
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
// 这个是处理父子类的问题,一般注入子类,那么后面会查看是否有父类,父类会返回为 sourceClass,再解析 sourceClass
// 但是配置类还是认为是最初的子类 configClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
可以看到这些被 @Import 的普通类虽然暂时没有被转换 BD 注入容器,但是被加入了一个集合。
下面转换到 ConfigurationClassPostProcessor
类中来,
方法:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
// ... 略
do {
// 这里是配置类最初始的调用解析
parser.parse(candidates);
parser.validate();
// 这里获取的就是上面那个集合
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());
}
// 这里, 内部就讲这些 @Import 的普通类给注入容器了
this.reader.loadBeanDefinitions(configClasses);
}
3. 注入逻辑
待续