程序员社区

Spring 源码解析 —— 配置类处理流程(@Configuration)


title: Spring 源码解析 —— 配置类处理流程(@Configuration)
date: 2021/01/14 13:36
remark: Spring 版本为 5.2.5


简介

@Configuration 注解的主要作用就是向容器中注入一些 bean,所以很容易想到 Spring 是通过 BeanFactoryPostProcessor 来实现对配置类进行处理的,处理类为 ConfigurationClassPostProcessor,我们看一下他的体系图:

Spring 源码解析 —— 配置类处理流程(@Configuration)插图

ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 在刷新容器的时候调用它的 postProcessBeanDefinitionRegistry() 方法。

Spring 源码解析 —— 配置类处理流程(@Configuration)插图1

我们先不着急继续往下看代码,先看下下面的图简单了解一下这个方法的整个的流程:

Spring 源码解析 —— 配置类处理流程(@Configuration)插图2

开始吧 ConfigurationClassPostProcessor#processConfigBeanDefinitions

本部分测试demo,建议先看一下 demo,这样更好理解。

Spring 源码解析 —— 配置类处理流程(@Configuration)插图3
Spring 源码解析 —— 配置类处理流程(@Configuration)插图4

tag1 parser.parse(candidates)

Spring 源码解析 —— 配置类处理流程(@Configuration)插图5
Spring 源码解析 —— 配置类处理流程(@Configuration)插图6

tag1.1 conditionEvaluator.shouldSkip

Spring 源码解析 —— 配置类处理流程(@Configuration)插图7

tag1.2 doProcessConfigurationClass(configClass, sourceClass, filter)

Spring 源码解析 —— 配置类处理流程(@Configuration)插图8
Spring 源码解析 —— 配置类处理流程(@Configuration)插图9

@ComponentScan 会直接将扫描到的实体加入容器中。

Spring 源码解析 —— 配置类处理流程(@Configuration)插图10
Spring 源码解析 —— 配置类处理流程(@Configuration)插图11

tag1.2.1 processMemberClasses(configClass, sourceClass, filter)

Spring 源码解析 —— 配置类处理流程(@Configuration)插图12

tag1.2.2 processPropertySource(propertySource)

Spring 源码解析 —— 配置类处理流程(@Configuration)插图13
Spring 源码解析 —— 配置类处理流程(@Configuration)插图14

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"));
    }
}
Spring 源码解析 —— 配置类处理流程(@Configuration)插图15
Spring 源码解析 —— 配置类处理流程(@Configuration)插图16

注意:此时引入的新的配置类的 bd (除 @ComponentScan 扫描进来的)还没有放进 bdRegistry 中,只是存放在了 ConfigurationClassParser.configurationClasses 中了,之后要由 reader 将其 bd 放进 bdRegistry 中

tag2 parser.validate()

Spring 源码解析 —— 配置类处理流程(@Configuration)插图17

tag3 reader.loadBeanDefinitions(configClasses)

Spring 源码解析 —— 配置类处理流程(@Configuration)插图18

tag3.1 registerBeanDefinitionForImportedConfigurationClass(configClass);

Spring 源码解析 —— 配置类处理流程(@Configuration)插图19

tag3.2 loadBeanDefinitionsForBeanMethod(beanMethod);

Spring 源码解析 —— 配置类处理流程(@Configuration)插图20
Spring 源码解析 —— 配置类处理流程(@Configuration)插图21
Spring 源码解析 —— 配置类处理流程(@Configuration)插图22

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

Spring 源码解析 —— 配置类处理流程(@Configuration)插图23

他把标注这 4 个注解作为配置类我可以理解(有可能是通过配置文件或其他方式注入的 bean),但搞不懂为啥 spring 要给普通的标注那 4 个注解(没有@Bean)方法的 Lite 模式,Lite 模式作用于的是 @Bean 标注的方法之间互相引用,对这 4 个注解好像没啥用吧。可能是处理过的总要给一个模式,来告诉 spring 已经处理过这个类了,所以就把 Lite 作为默认模式了。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring 源码解析 —— 配置类处理流程(@Configuration)

一个分享Java & Python知识的社区