【Spring】简述@Configuration配置类注册Bea

2019-11-26  本文已影响0人  Trust_FreeDom

概述

本文以SpringBoot应用为基础,尝试分析基于注解@Configuration的配置类是如何向Spring容器注册BeanDefinition的过程

其中主要分析了 ConfigurationClassPostProcessor 这个BeanDefinitionRegistryPostProcessor 即Bean定义注册后置处理器,在Spring启动过程中对@Configuration配置类的处理,主要体现在 解析并发现所有配置类,处理配置类的相关逻辑(如配置类上的@ComponentScan、@Import、@Bean注解等),注册其中的BeanDefinition

SpringBoot版本:2.0.9.RELEASE

Spring版本:5.0.13.RELEASE

ConfigurationClassPostProcessor如何被引入

首先看一下ConfigurationClassPostProcessor的类继承关系

ConfigurationClassPostProcessor继承关系

从红框中可以看出ConfigurationClassPostProcessorBeanDefinitionRegistryPostProcessor接口的实现类,即是一个Bean定义注册的后置处理器,会在Spring容器启动时被调用,具体时机为

// 调用链
AbstractApplicationContext.refresh()
    => invokeBeanFactoryPostProcessors()
    => PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()

invokeBeanFactoryPostProcessors()会先调用所有的BeanDefinitionRegistryPostProcessor之后,再调用所有的BeanFactoryPostProcessor

ConfigurationClassPostProcessor又是如何被引入Spring的呢??

SpringBoot应用会在ApplicationContext应用上下文被创建的构造函数中new AnnotatedBeanDefinitionReader这个用于注册基于注解的BeanDefinition的Reader,在其构造中又会调用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)使用工具类向Spring容器中注册一些所谓的注解配置处理器,其中就包含ConfigurationClassPostProcessor

// ConfigurationClassPostProcessor被注册
AnnotationConfigServletWebServerApplicationContext构造
    => new AnnotatedBeanDefinitionReader(registry)
        => AnnotationConfigUtils.registerAnnotationConfigProcessors(registry)
            => 注册ConfigurationClassPostProcessor到Spring容器

ConfigurationClassPostProcessor处理过程简述

首先,ConfigurationClassPostProcessor后置处理器的处理入口为postProcessBeanDefinitionRegistry()方法。其主要使用了ConfigurationClassParser配置类解析器解析@Configuration配置类上的诸如@ComponentScan@Import@Bean等注解,并尝试发现所有的配置类;还使用了ConfigurationClassBeanDefinitionReader注册所发现的所有配置类中的所有Bean定义;结束执行的条件是所有配置类都被发现和处理,相应的bean定义注册到容器

大致流程如下:

1、通过BeanDefinitionRegistry查找当前Spring容器中所有BeanDefinition

2、通过ConfigurationClassUtils.checkConfigurationClassCandidate() 检查BeanDefinition是否为 “完全配置类”“简化配置类”,并对配置类做标记,放入集合待后续处理

Spring配置类的分类可以 参考

3、通过 ConfigurationClassParser解析器 parse解析配置类集合,尝试通过它们找到其它配置类

4、使用 ConfigurationClassBeanDefinitionReader 注册通过所发现的配置类中找到的所有beanDefinition

5、处理完一轮配置类后,查看BeanDefinitionRegistry中是否存在新加载的且还未被处理过的 “完全配置类”“简化配置类”,有的话继续上面步骤

其中第3、4步后面重点分析

ConfigurationClassParser#parse():解析构建配置类

对于SpringBoot应用来说,参与解析的种子配置文件即为SpringBoot的Application启动类

解析构建配置类流程

通过ConfigurationClassParser解析器parse解析配置类集合,尝试通过它们找到其它配置类

解析构建配置类源码分析

ConfigurationClassParser#processConfigurationClass()

包含了处理单个配置类的大体流程,先根据ConfigurationPhase.PARSE_CONFIGURATION解析配置阶段的@Conditional条件判断当前配置类是否应该解析,之后调用ConfigurationClassParser#doProcessConfigurationClass()循环解析配置类,直到不存在未处理过的父类

/**
 * 解析单个配置类
 * 解析的最后会将当前配置类放到configurationClasses
 */
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    /**
     * 根据@Conditional条件判断是否跳过配置类
     * 注意:当前这个PARSE_CONFIGURATION解析配置阶段只会使用这个阶段的@Conditional条件,有些REGISTER_BEAN注册beanDefinition阶段的条件不会在此时使用
     */
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    // 如果configClass在已经分析处理的配置类记录中已存在
    if (existingClass != null) {
        //如果配置类是被@Import注册的,return
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            // Otherwise ignore new imported config class; existing non-imported class overrides it.
            return;
        }
        // 否则,清除老的记录,在来一遍
        else {
            // Explicit bean definition found, probably replacing an import.
            // Let's remove the old one and go with the new one.
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    // Recursively process the configuration class and its superclass hierarchy.
    /**
     * 递归处理配置类及其超类层次结构
     * 从当前配置类configClass开始向上沿着类继承结构逐层执行doProcessConfigurationClass,直到遇到的父类是由Java提供的类结束循环
     */
    SourceClass sourceClass = asSourceClass(configClass);
    /**
     * 循环处理配置类configClass直到sourceClass变为null,即父类为null
     * doProcessConfigurationClass的返回值是其参数configClass的父类
     * 如果该父类是由Java提供的类或者已经处理过,返回null
     */
    do {
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}

ConfigurationClassParser#doProcessConfigurationClass():真正解析配置类

通过解析配置类上的注解、内部成员类和方法构建一个完整的ConfigurationClass配置类,过程中如果发现了新的配置类可以重复调用此方法

真正解析过程中会处理成员内部类@PropertySource@ComponentScan@Import@ImportSource@Bean方法等,流程如下:

/**
 * Apply processing and build a complete {@link ConfigurationClass} by reading the
 * annotations, members and methods from the source class. This method can be called
 * multiple times as relevant sources are discovered.
 * @param configClass the configuration class being build
 * @param sourceClass a source class
 * @return the superclass, or {@code null} if none found or previously processed
 */
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    // Recursively process any member (nested) classes first
    /**
     * 1、处理配置类的成员类(配置类内嵌套定义的类)
     * 内部嵌套类也可能是配置类,遍历这些成员类,检查是否为"完全/简化配置类"
     * 有的话,调用processConfigurationClass()处理它们,最终将配置类放入configurationClasses集合
     */
    processMemberClasses(configClass, sourceClass);

    // Process any @PropertySource annotations
    /**
     * 2、处理 @PropertySource
     * 将找到的PropertySource添加到environment的PropertySource集合
     */
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    /**
     * 3、处理 @ComponentScan
     * 处理用户手工添加的@ComponentScan,SpringBoot创建ApplicationContext时的ClassPathBeanDefinitionScanner是为了扫描启动类下的包
     * 为的是找到满足条件的@ComponentScan,即@Component相关的组件,先扫描一下,扫描到的就注册为BeanDefinition
     * 看其中是否还有配置类,有的话parse()继续分析处理,配置类添加到configurationClasses集合
     */
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    // 如果当前配置类上有@ComponentScan,且使用REGISTER_BEAN注册beanDefinition的条件判断也不跳过的话
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            // 立即扫描,扫描到的就注册为BeanDefinition,并获得扫描到的所有beanDefinition
            // 在处理SpringBoot启动类上的@ComponentScan时,虽然指指定了excludeFilters,但会根据启动类所在包推测basePackage,就会扫描到SpringBoot启动类包以下的Bean并注册
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            // 检查扫描到的beanDefinition中是否有配置类,有的话parse()继续分析处理,,配置类添加到configurationClasses集合
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    /**
     * 4、处理 @Import
     * (1)处理ImportSelector
     * 如果是DeferredImportSelector,如SpringBoot的自动配置导入,添加到deferredImportSelectors,延迟进行processImports()
     * 其它通过ImportSelector找到的类,继续调用processImports(),要么是@Configuration配置类继续解析,要么是普通组件导入Spring容器
     * (2)处理ImportBeanDefinitionRegistrar
     * 调用当前配置类的addImportBeanDefinitionRegistrar(),后面委托它注册其它bean定义
     * (3)其它
     * 调用processConfigurationClass()继续解析,最终要么是配置类放入configurationClasses,要么是普通组件导入Spring容器
     */
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    /**
     * 5、处理 @ImportResource
     * 添加到配置类的importedResources集合,后续loadBeanDefinitions()加载bean定义时再让这些导入BeanDefinitionReader自行读取bean定义
     */
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    /**
     * 6、处理个别@Bean方法
     * 获取所有@Bean方法,并添加到配置类的beanMethods集合
     */
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    /**
     * 7、处理配置类接口上的default methods
     */
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    /**
     * 8、检查父类是否需要处理,如果父类需要处理返回父类,否则返回null
     * 如果存在父类,且不在knownSuperclasses已经分析过的父类列表里,返回并继续分析
     */
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}

ConfigurationClassBeanDefinitionReader#loadBeanDefinitions():读取配置类,基于配置信息注册BeanDefinition

读取配置类,基于配置信息注册BeanDefinition流程

在上面解析配置类的过程中,除了构建了一个完整的ConfigurationClass配置类,其实已经向BeanDefinitionRegistry中添加了一些beanDefinition了,比如在处理@ComponentScan时,扫描到的@Component相关组件就已经注册了

ConfigurationClassBeanDefinitionReader会继续读取已经构建好的ConfigurationClass配置类中的成员变量,从而注册beanDefinition

构建好的ConfigurationClass配置类中在本阶段可用的成员变量包括:

  1. Set<BeanMethod> beanMethods: @Bean的方法
  2. Map<String, Class<? extends BeanDefinitionReader>> importedResources:配置类上@ImportResource注解的类存入此集合,会使用BeanDefinitionReader读取Resource中的BeanDefinition并注册
  3. Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars:ImportBeanDefinitionRegistrar集合

通过构建好的配置类的配置信息,使用ConfigurationClassBeanDefinitionReader注册所有能够读取到的beanDefinition:

读取配置类,基于配置信息注册BeanDefinition源码分析

/**
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 * 读取特定配置类,根据配置信息注册bean definitions
 */
private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    /**
     * 根据ConfigurationPhase.REGISTER_BEAN阶段条件判断配置类是否需要跳过
     * 循环判断配置类以及导入配置类的类,使用ConfigurationPhase.REGISTER_BEAN阶段条件判断是否需要跳过
     * 只要配置类或导入配置类的类需要跳过即返回跳过
     */
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    // 1、如果当前配置类是通过内部类导入 或 @Import导入,将配置类自身注册为beanDefinition
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }

    // 2、注册配置类所有@Bean方法为beanDefinition
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    // 3、注册由@ImportedResources来的beanDefinition
    // 即通过其它类型Resource的BeanDefinitionReader读取BeanDefinition并注册
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

    // 4、注册由ImportBeanDefinitionRegistrars来的beanDefinition
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

思维导图

请放大观看


mind

参考

Spring BeanDefinitionRegistryPostProcessor:ConfigurationClassPostProcessor

Spring 工具类 ConfigurationClassParser 分析得到配置类

Spring 配置类的分类

上一篇下一篇

猜你喜欢

热点阅读