Spring源码阅读 - ConfigurationClassPostProcessor 配置类处理

1. 概述

当使用 spring-context 搭建简单 demo 时,调用 BeanDefinitionRegistryPostProcessor 第一个就是 ConfigurationClassPostProcessor
image

同时注意到它干了什么,它 getBean 立即将其转换为 Bean / 实例化了

currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));

2. 流程

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // 一致性哈希,计算了一致性哈希或者没有重写 hashCode 但是调用了,那么这个对象的 synchronized 锁就不能再处于无锁状态了
    int registryId = System.identityHashCode(registry);
    // 这个是涉及多个上下文环境的那种处理吗
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    // 为什么上下分两个 ?
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    // 所有的 BD,注意这里包含 BeanFactoryPostProcessor , 当前类也包含,也就是说前面的 getBean 获取当前这个 BeanFactoryPostProcessor 并未将 BD 给删除
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 由于上面是获取容器内所有 BD,因此可预见的是当前方法只会走一次,即便后续查找到其他 BD 也需要进行解析,也不会走当前方法
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 属性非空说明被解析过了
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        // 配置类: Configuration、ComponentScan、Bean、Import、ImportResource
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // Return immediately if no @Configuration classes were found
    // 为空,没有配置类需要解析,直接返回
    if (configCandidates.isEmpty()) {
        return;
    }

    // Sort by previously determined @Order value, if applicable
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // Detect any custom bean name generation strategy supplied through the enclosing application context
    // BeanName 生成器, 那么如何自定义呢
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // Parse each @Configuration class
    // 处理 @Configuration 注解的类
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
        // 解析配置类,从上下可分析出来,即便类似 @ComponentScan 查找到其他 BD,肯定也是直接进 Parser 内部的代码进行配置类解析了
        parser.parse(candidates);
        parser.validate();

        // 这里面应该是 Parser 记录的解析过的配置类
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        // 去除已经解析过的,这个实际主要是用在后面 loadBeanDefinitions 去除不必要的 BD, removeAll 是为了减少判断
        //  alreadyParsed 是上一次循环已经解析过的,去除上一次解析过的就是这一次解析过的,主要是 Parser 是复用的,所以需要 removeAll
        configClasses.removeAll(alreadyParsed);

        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 额, 这里对类似执行了一边 shouldSkip, 将不满足条件注入的BD从容器去除
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

        candidates.clear();
        // 什么情况会发生注入BD进了容器,但是却没有被解析呢?
        // 下面逻辑直接略, 反正是筛选出没有解析过的 BD 放到 candidates 循环过来解析
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    // @Configuration 配置类需要,继承 ImportAware 接口即会传入配置类 @Import 注解的元数据信息
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

3. 具体配置类解析流程

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 一般是进入这里,其他的暂略
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    this.deferredImportSelectorHandler.process();
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 这里也判断一遍, 在容器传入启动类使用 Reader 解析时传入的 ConfigurationPhase 为 null, 默认也是作为 PARSE_CONFIGURATION 进行解析的
    //    就是 Conditional 注解的解析,满足条件才会进行解析
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
    // configurationClasses 保存着已解析的配置类
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    // 已经被解析过了
    if (existingClass != null) {
        // 那这个配置类是否是通过一个配置类的 @Import 导入的 或者说是一个配置类的内部类从而导入的
        //    内部类的情况是咋样?
        if (configClass.isImported()) {
            // 已存在的这个配置类也是被一个配置类 @Import 导入的或 ...
            if (existingClass.isImported()) {
                // 合并,说明相同的一个类被两个配置类均 @Import 了,那么后面会造成什么情况?若其中一个配置类的@Conditional 不满足那么它就不会被注入容器?
                existingClass.mergeImportedBy(configClass);
            }
            // Otherwise ignore new imported config class; existing non-imported class overrides it.
            // 说明这个类即被 @Import 了, 又被通过其他方式注入了, 略, 也即被其他方式注入的优先
            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.
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        // 这个是处理父子类的问题,一般注入子类,那么后面会查看是否有父类,父类会返回为 sourceClass,再解析 sourceClass
        //    但是配置类还是认为是最初的子类 configClass
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    // 处理的配置类,可见这里就可能漏掉一些类,比如上面处理 configClass 可能返回其他 sourceClass, 那么这些被处理的 sourceClass 就没有被加入集合
    this.configurationClasses.put(configClass, configClass);
}

@Nullable
protected final SourceClass doProcessConfigurationClass(
        ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
        throws IOException {

    // @Configuration、@... 等都是继承 Component
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        // 处理内部类的,也是暂略
        processMemberClasses(configClass, sourceClass, filter);
    }

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    // REGISTER_BEAN 阶段,那么和 PARSE_CONFIGURATION 阶段的不同?
    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
            // 指定包扫描,扫描包及其子包,实际最后还是使用 ClassPathBeanDefinitionScanner 来进行扫描的,MyBatis 扫描接口好像也是使用它扫描的
            //    ClassPathBeanDefinitionScanner 扫描到后会先立即注入BD,也就是这里返回的都是已经注入的BD
            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
            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
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // Process any @ImportResource annotations
    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
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    // 若配置类作为子类,继承了一个父类,还会继续解析父类的数据
    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;
}

4. 几个点

  1. 配置类:@Configuration、@Component、@ComponentScan、@Import、@ImportResource

    • CONFIGURATION_CLASS_FULL:@Configuration + proxyBeanMethods 为 true(默认)
    • CONFIGURATION_CLASS_LITE:@Configuration + proxyBeanMethods 为 false、@Component、@ComponentScan、@Import、@ImportResource
    • 这些都是在 org.springframework.context.annotation.ConfigurationClassUtils 配置和处理的
  2. 问题:BeanDefinition 的分类?

  3. 处理的几个关键的注解:@Component、@ComponentScan、@Import、@ImportResource、@Bean

  4. ConfigurationClassPostProcessor

    • 继承 BeanDefinitionRegistryPostProcessor 接口
    • 配置类的处理,特别是 @ComponentScan 的处理
  5. ConfigurationClassParser

    • ConfigurationClassPostProcessor 实际是使用了一个这个实例进行具体的配置类解析的
posted @ 2022-04-08 23:38  YangDanMua  阅读(52)  评论(0编辑  收藏  举报