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


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


简介

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

image

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

image

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

image

开始吧 ConfigurationClassPostProcessor#processConfigBeanDefinitions

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

image
image

tag1 parser.parse(candidates)

image
image

tag1.1 conditionEvaluator.shouldSkip

image

tag1.2 doProcessConfigurationClass(configClass, sourceClass, filter)

image
image

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

image
image

tag1.2.1 processMemberClasses(configClass, sourceClass, filter)

image

tag1.2.2 processPropertySource(propertySource)

image
image

tag1.2.3 processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

先看下 getImports(sourceClass) 方法:

private Set getImports(SourceClass sourceClass) throws IOException {
    Set imports = new LinkedHashSet<>();
    Set visited = new LinkedHashSet<>();
    collectImports(sourceClass, imports, visited);
    return imports;
}

// 递归的获取当前配置类上的 @Import 注解(因为注解上也可能有 @Import 注解)
private void collectImports(SourceClass sourceClass, Set imports, Set 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"));
    }
}
image
image

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

tag2 parser.validate()

image

tag3 reader.loadBeanDefinitions(configClasses)

image

tag3.1 registerBeanDefinitionForImportedConfigurationClass(configClass);

image

tag3.2 loadBeanDefinitionsForBeanMethod(beanMethod);

image
image
image

tag3.3 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

简单到无以复加

private void loadBeanDefinitionsFromRegistrars(Map 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

image

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

你可能感兴趣的:(Spring 源码解析 —— 配置类处理流程(@Configuration))