title: Spring 源码解析 —— 配置类处理流程(@Configuration)
date: 2021/01/14 13:36
remark: Spring 版本为 5.2.5
简介
@Configuration 注解的主要作用就是向容器中注入一些 bean,所以很容易想到 Spring 是通过 BeanFactoryPostProcessor 来实现对配置类进行处理的,处理类为 ConfigurationClassPostProcessor,我们看一下他的体系图:
ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 在刷新容器的时候调用它的 postProcessBeanDefinitionRegistry() 方法。
我们先不着急继续往下看代码,先看下下面的图简单了解一下这个方法的整个的流程:
开始吧 ConfigurationClassPostProcessor#processConfigBeanDefinitions
本部分测试demo,建议先看一下 demo,这样更好理解。
tag1 parser.parse(candidates)
tag1.1 conditionEvaluator.shouldSkip
tag1.2 doProcessConfigurationClass(configClass, sourceClass, filter)
@ComponentScan 会直接将扫描到的实体加入容器中。
tag1.2.1 processMemberClasses(configClass, sourceClass, filter)
tag1.2.2 processPropertySource(propertySource)
tag1.2.3 processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
先看下 getImports(sourceClass) 方法:
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
// 递归的获取当前配置类上的 @Import 注解(因为注解上也可能有 @Import 注解)
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.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
注意:此时引入的新的配置类的 bd (除 @ComponentScan 扫描进来的)还没有放进 bdRegistry 中,只是存放在了 ConfigurationClassParser.configurationClasses 中了,之后要由 reader 将其 bd 放进 bdRegistry 中
tag2 parser.validate()
tag3 reader.loadBeanDefinitions(configClasses)
tag3.1 registerBeanDefinitionForImportedConfigurationClass(configClass);
tag3.2 loadBeanDefinitionsForBeanMethod(beanMethod);
tag3.3 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
简单到无以复加
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
冷知识:Full模式和Lite模式
Spring配置类的Full模式和Lite模式
你真的懂Spring Java Config 吗?Full @Configuration vs lite @Bean mode
他把标注这 4 个注解作为配置类我可以理解(有可能是通过配置文件或其他方式注入的 bean),但搞不懂为啥 spring 要给普通的标注那 4 个注解(没有@Bean)方法的 Lite 模式,Lite 模式作用于的是 @Bean 标注的方法之间互相引用,对这 4 个注解好像没啥用吧。可能是处理过的总要给一个模式,来告诉 spring 已经处理过这个类了,所以就把 Lite 作为默认模式了。