Spring中的ConfigurationClassBeanDefinitionReader

Spring中的ConfigurationClassParser一文中,提到了会把一些解析结果,如bean方法、需要导入的配置文件资源和bean定义注册器保存在ConfigurationClass对象的属性中。后续ConfigurationClassPostProcessor会调用ConfigurationClassBeanDefinitionReader来处理。本文就来分析一下是怎么处理的。


该类ConfigurationClassBeanDefinitionReaderConfigurationClassParser类共同组成了ConfigurationClassPostProcessor的registry的后置操作中的核心实现。

loadBeanDefinitions方法

ConfigurationClassPostProcessor中会直接调用该方法。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    // 创建条件判断器
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    // 遍历所要处理的配置类
    for (ConfigurationClass configClass : configurationModel) {
        // 加载配置类中的bean定义
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

该方法遍历每个配置类,并调用loadBeanDefinitionsForConfigurationClass方法。

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    // 判断类是不是可以跳过
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        // 判断bean是不是存在
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            // 移除bean定义
            this.registry.removeBeanDefinition(beanName);
        }
        // 移除配置类中的bean
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    // 如果当前配置类是否是被其他类导入的
    if (configClass.isImported()) {
        // 注册配置bean
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    // 加载@Bean方法
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    // 加载@ImportResources导入的bean信息
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    /*
     * 注意这里处理ImportBeanDefinitionRegistrar的实现类是由该类上的@Import引入的,
     * 而不是直接处理该类的实现了ImportBeanDefinitionRegistrar的内部类。
     */
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

在该方法中,主要做了下面几件操作:

  • 判断该类是否满足条件注解中的要求;
  • 注册配置bean;
  • 处理bean方法;
  • 处理导入的配置文件资源;
  • 从注册器中加载bean定义;

条件判断

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
private class TrackedConditionEvaluator {

    private final Map<ConfigurationClass, Boolean> skipped = new HashMap<>();

    public boolean shouldSkip(ConfigurationClass configClass) {
        // 查询缓存
        Boolean skip = this.skipped.get(configClass);
        if (skip == null) {
            // 判断当前类是否被其他配置类导入
            if (configClass.isImported()) {
                boolean allSkipped = true;
                // 遍历导入当前配置类的其他配置类
                for (ConfigurationClass importedBy : configClass.getImportedBy()) {
                    // 递归判断是否应该跳过导入当前类的其他配置类
                    if (!shouldSkip(importedBy)) {
                        allSkipped = false;
                        break;
                    }
                }
                // 只有所有导入该配置类的其他配置类都需要跳过时,才跳过当前类
                if (allSkipped) {
                    // The config classes that imported this one were all skipped, therefore we are skipped...
                    skip = true;
                }
            }
            if (skip == null) {
                // 通过条件判断器来判断是不是应该跳过
                skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
            }
            // 将处理结果加入缓存中
            this.skipped.put(configClass, skip);
        }
        return skip;
    }
}

TrackedConditionEvaluator类是ConfigurationClassBeanDefinitionReader的一个内部类,主要目的是为了封装ConditionEvaluator类。前者在后者的基础上,提供了缓存的功能,以及递归判断了引入当前配置类的类是不是满足条件。实际的判断操作还是通过ConditionEvaluator来实现的。

java
spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
    return shouldSkip(metadata, null);
}
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 如果没被@Conditional注解修饰,则不跳过
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }

    if (phase == null) {
        if (metadata instanceof AnnotationMetadata &&
                // 如果是配置类
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            // 递归处理
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        // 递归处理
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }

    List<Condition> conditions = new ArrayList<>();
    // 获取Condition类型信息
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        // 遍历类
        for (String conditionClass : conditionClasses) {
            // 创建Condition对象
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            // 添加到集合中
            conditions.add(condition);
        }
    }

    // 对Condition对象进行排序
    AnnotationAwareOrderComparator.sort(conditions);

    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            // 获取Condition对象生效的阶段
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }
        /*
         * 如果存在生效阶段,则先比较和目标阶段是否一致,如果不一致,则不做处理。
         * 如果match返回false,说明应该skip
         * 在Spring Boot中大量使用了@Conditional注解,而且定义了很多Condition类,可以转移到Spring Boot的源码中进行分析。
         */
        if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
            return true;
        }
    }

    return false;
}

这个方法主要在获取@Conditional注解中配置的Condition对象,然后调用这些对象的matches方法,如果返回了false,则会认为该配置类应该被跳过。

这个功能在Spring Boot中大量被使用,而且@Conditional有很多衍生注解,比如@ConditionalOnMissingBean@ConditionalOnClass,主要是避免了用户设置@Conditional注解的Condition类型,从而满足开箱即用的特性。

注册配置bean

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    // 获取配置类的元数据
    AnnotationMetadata metadata = configClass.getMetadata();
    // 创建bean定义
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

    // 解析作用域元数据
    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());
    // 生成bean的名称
    String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
    // 处理一些常见的注解
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
    // 应用作用域代理模式
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 注册bean定义
    this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
    configClass.setBeanName(configBeanName);

    if (logger.isTraceEnabled()) {
        logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
    }
}

仔细一看,就会发现处理过程和ClassPathBeanDefinitionScannerdoScan方法的for循环中的逻辑差不多,请参考Spring中的ClassPathBeanDefinitionScanner一文,这里不再赘述。

处理bean方法

Spring会将配置类中被@Bean注解修饰的方法抽象为bean注册到bean工厂中。

loadBeanDefinitionsForBeanMethod
isOverriddenByExistingDefinition
<
>
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();

    // Do we need to mark the bean as skipped by its condition?
    // 判断bean方法是否能跳过
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
        configClass.skippedBeanMethods.add(methodName);
        return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
        return;
    }

    // 获取@Bean注解的属性
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");

    // Consider name and any aliases
    // 获取其name属性
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    // 默认以方法名作为bean的名称,如果指定了name,那么以第一个元素作为bean的名称
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

    // Register aliases even when overridden
    // 将所有名称设置为别名
    for (String alias : names) {
        // 设置别名
        this.registry.registerAlias(beanName, alias);
    }

    // Has this effectively been overridden before (e.g. via XML)?
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
        // 判断bean方法的名称是否和所在配置类的bean名称相同,
        if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
            // 如果相同则抛出异常
            throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
                    beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
                    "' clashes with bean name for containing configuration class; please make those names unique!");
        }
        return;
    }

    // 创建bean定义对象
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
    // 设置bean定义来源
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

    if (metadata.isStatic()) { // 如果是静态方法
        // static @Bean method
        if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
            // 设置bean的类型
            beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
        }
        else {
            // 设置bean的类型
            beanDef.setBeanClassName(configClass.getMetadata().getClassName());
        }
        // 设置创建bean的工厂方法的名称
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    else { // 非静态方法
        // instance @Bean method
        // 将配置类的bean名称作为工厂bean的名称
        beanDef.setFactoryBeanName(configClass.getBeanName());
        // 设置创建bean的工厂方法的名称
        beanDef.setUniqueFactoryMethodName(methodName);
    }

    if (metadata instanceof StandardMethodMetadata) {
        beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
    }

    // 设置装配模式
    beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
            SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

    AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

    // 获取@Bean注解的autowire属性
    Autowire autowire = bean.getEnum("autowire");
    if (autowire.isAutowire()) {
        // 设置自动装配模式
        beanDef.setAutowireMode(autowire.value());
    }

    // 是否作为自动装配的候选bean
    boolean autowireCandidate = bean.getBoolean("autowireCandidate");
    if (!autowireCandidate) {
        beanDef.setAutowireCandidate(false);
    }

    // 出去初始化方法名称
    String initMethodName = bean.getString("initMethod");
    if (StringUtils.hasText(initMethodName)) {
        beanDef.setInitMethodName(initMethodName);
    }

    // 获取销毁方法名称
    String destroyMethodName = bean.getString("destroyMethod");
    beanDef.setDestroyMethodName(destroyMethodName);

    // Consider scoping
    ScopedProxyMode proxyMode = ScopedProxyMode.NO;
    // 获取@Scope注解
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
    if (attributes != null) {
        beanDef.setScope(attributes.getString("value"));
        proxyMode = attributes.getEnum("proxyMode");
        if (proxyMode == ScopedProxyMode.DEFAULT) {
            proxyMode = ScopedProxyMode.NO;
        }
    }

    // Replace the original bean definition with the target one, if necessary
    BeanDefinition beanDefToRegister = beanDef;
    if (proxyMode != ScopedProxyMode.NO) {
        // 创建作用域代理bean的定义
        BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
                new BeanDefinitionHolder(beanDef, beanName), this.registry,
                proxyMode == ScopedProxyMode.TARGET_CLASS);
        // 创建配置类bean定义
        beanDefToRegister = new ConfigurationClassBeanDefinition(
                (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
    }


    // 注册bean定义
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
    // 先判断bean工厂中是否存在目标名称的bean
    if (!this.registry.containsBeanDefinition(beanName)) {
        return false;
    }
    // 根据bean名称获取已有的bean
    BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);

    // Is the existing bean definition one that was created from a configuration class?
    // -> allow the current bean method to override, since both are at second-pass level.
    // However, if the bean method is an overloaded case on the same configuration class,
    // preserve the existing bean definition.
    if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
        ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
        // 如果已存在的bean的类名称和bean方法所在配置类的名称相同
        if (ccbd.getMetadata().getClassName().equals(
                beanMethod.getConfigurationClass().getMetadata().getClassName())) {
            if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
                ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
            }
            return true;
        }
        else {
            return false;
        }
    }

    // A bean definition resulting from a component scan can be silently overridden
    // by an @Bean method, as of 4.2...
    if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
        return false;
    }

    // Has the existing bean definition bean marked as a framework-generated bean?
    // -> allow the current bean method to override it, since it is application-level
    if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
        return false;
    }

    // At this point, it's a top-level override (probably XML), just having been parsed
    // before configuration class processing kicks in...
    if (this.registry instanceof DefaultListableBeanFactory &&
            // 默认是true
            !((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {
        throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
                beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
    }
    return true;
}

尽管该方法很长,但是内部主要是在处理@Bean@Scope注解,最后一行代码才是真正的注册操作。

处理导入的配置文件资源

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
// 加载XML文件的配置
private void loadBeanDefinitionsFromImportedResources(
        Map<String, Class<? extends BeanDefinitionReader>> importedResources) {

    Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();

    // 遍历resource
    importedResources.forEach((resource, readerClass) -> {
        // Default reader selection necessary?
        // 确定使用哪种读取器,readerClass是@ImportResource注解中的reader属性。
        if (BeanDefinitionReader.class == readerClass) {
            // 忽略.groovy
            if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
                // When clearly asking for Groovy, that's what they'll get...
                readerClass = GroovyBeanDefinitionReader.class;
            }
            else if (shouldIgnoreXml) {
                throw new UnsupportedOperationException("XML support disabled");
            }
            else {
                // Primarily ".xml" files but for any other extension as well
                // 默认的读取器类型
                readerClass = XmlBeanDefinitionReader.class;
            }
        }

        // 获取缓存中的bean定义读取器
        BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
        if (reader == null) {
            try {
                // Instantiate the specified BeanDefinitionReader
                // 创建bean定义读取器对象
                reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
                // Delegate the current ResourceLoader to it if possible
                if (reader instanceof AbstractBeanDefinitionReader) {
                    AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
                    abdr.setResourceLoader(this.resourceLoader);
                    abdr.setEnvironment(this.environment);
                }
                // 将读取器对象加入缓存
                readerInstanceCache.put(readerClass, reader);
            }
            catch (Throwable ex) {
                throw new IllegalStateException(
                        "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
            }
        }

        // TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
        // 加载资源中的bean定义
        reader.loadBeanDefinitions(resource);
    });
}

由于这种方式使用的很少,所以不进一步分析具体reader的bean定义加载逻辑了。

处理注册器

java
spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
// 从注册器中加载bean
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    // 遍历注册器
    registrars.forEach((registrar, metadata) ->
            // 执行注册器
            registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

这里遍历所有的注册器,并调用它的registerBeanDefinitions方法来注册bean。

总结

本文分析了ConfigurationClassBeanDefinitionReader的主要实现原理。 通过本文知道了尤其常用的bean方法是怎么被注册的,以及不太常用的外部配置文件和注册器又是怎么注册的bean。