深入解析ConfigurationClassPostProcessor:配置类处理器的工作机制

ConfigurationClassPostProcessor是Spring框架中用于处理Java配置类的关键组件。本文将深入解析ConfigurationClassPostProcessor的源码,详细介绍其工作原理和实现细节。我们将从其基本概念和作用出发,逐步剖析ConfigurationClassPostProcessor的初始化、配置类解析、Bean定义注册等核心步骤。

概述

定义和作用

ConfigurationClassPostProcessor 是 Spring 框架中的一个关键组件,用于处理 Java 配置类(使用 @Configuration 注解的类)。它的主要作用是解析这些配置类并注册相应的 Bean 定义到 Spring 容器中。

ConfigurationClassPostProcessor 是一个实现了 BeanDefinitionRegistryPostProcessor 接口的类。

image-20240612111846310

ConfigurationClassPostProcessor 的主要作用包括以下几个方面:

  1. 解析 @Configuration:负责扫描和解析所有标注了 @Configuration 注解的类。@Configuration 类通常包含一个或多个 @Bean 方法,这些方法定义了需要注册到 Spring 容器中的 Bean。
  2. 处理 @Bean 方法:在解析 @Configuration 类时,ConfigurationClassPostProcessor 会处理其中的 @Bean 方法,并将这些方法返回的对象注册为 Spring 容器中的 Bean。每个 @Bean 方法相当于一个工厂方法,用于创建和配置特定类型的 Bean。
  3. 解析 @ComponentScan:此注解用于指定包扫描路径,以自动发现和注册使用 @Component、@Service、@Repository 等注解的 Bean。ConfigurationClassPostProcessor 会处理这些扫描路径,并将扫描到的 Bean 定义注册到 Spring 容器中。
  4. 处理 @Import 注解:此注解用于导入其他配置类或特定的 ImportSelector 和 ImportBeanDefinitionRegistrar 实现。ConfigurationClassPostProcessor 会处理这些导入,并将相关的 Bean 定义注册到 Spring 容器中。
  5. 处理 @PropertySource 注解:此注解用于加载外部属性文件。ConfigurationClassPostProcessor 会解析这些注解并将属性文件的内容加载到 Spring 的环境中,以便在配置类或 @Value 注解中使用。
  6. 处理 @ImportResource 注解:此注解用于将外部的 XML 配置文件导入到 Spring 应用上下文中,这样可以将 XML 配置和 Java 配置结合使用。

常见使用场景及其影响

1.Java 配置类替代 XML 配置

使用场景:开发者希望使用 Java 配置类来替代传统的 XML 配置文件,以便利用 Java 语言的类型安全性和 IDE 提供的代码补全等功能。

影响:提高了配置的可读性和可维护性。Java 配置类能够使用面向对象的设计模式,便于重构和调试。

2.组件扫描

使用场景:通过 @ComponentScan 注解自动扫描特定包中的组件,简化 Bean 注册。

影响:减少手动注册 Bean 的工作量,自动发现和注册组件,使应用更具模块化。

3.导入其他配置类

使用场景:通过 @Import 注解导入其他配置类,实现配置的模块化和分离。

影响:可以将配置拆分成多个类,便于管理和维护,并且可以重用配置。

4.加载外部 XML 配置

使用场景:通过 @ImportResource 注解加载外部的 XML 配置文件,实现 XML 配置和 Java 配置的混合使用。

影响:在逐步迁移到 Java 配置时非常有用,可以同时使用现有的 XML 配置和新的 Java 配置。

5.属性文件加载

使用场景:通过 @PropertySource 注解加载外部属性文件,使属性配置更加灵活。

影响:能够将配置属性从代码中分离出来,方便环境配置和管理。

初始化过程

通过对于上下文 AnnotationConfigApplicationContext 的学习,我们知道,在 AnnotationConfigApplicationContext 的构造方法中,Spring 首先就会自动的注册 5 个 Bean,其中还有两个有关 Spring 事件相关的我们先忽略,剩下的还有三个重要的类型:

  1. ConfigurationClassPostProcessor:处理 @Configuration、@ComponentScan、@Import、@ImportResource、@PropertySource 注解。
  2. AutowiredAnnotationBeanPostProcessor:处理 @Autowired、@Value、@Inject 注解。
  3. CommonAnnotationBeanPostProcessor:处理 JSR-250 规范中的注解,比如 @Resource、@PostConstruct、@PreDestory 等。

这三个 Bean 中,ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的实现类,而 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 都是 BeanPostProcessor 的实现类。

那么再深入一下源码,找到真正注册这五个 Bean 的地方:

AnnotationConfigApplicationContext 的无参构造会初始化 AnnotatedBeanDefinitionReader 对象,而注册这五个 Bean 的时机就是在 AnnotatedBeanDefinitionReader 的构造方法中:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // 就是这里!
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    ...
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注册 org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    // ConfigurationClassPostProcessor 用来处理 Configuration 注解
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注册 org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    // AutowiredAnnotationBeanPostProcessor 用来处理 Autowired 注解
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    
    // 注册 org.springframework.context.annotation.internalCommonAnnotationProcessor
    // CommonAnnotationBeanPostProcessor 用来处理通用注解,例如:@Resource
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    
    // 注册 org.springframework.context.event.internalEventListenerProcessor
    // 与 EventListener 有关
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 注册 org.springframework.context.event.internalEventListenerFactory
    // 还是与 EventListener 有关
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

源码剖析

基于前面上下文 refresh 的学习内容,invokeBeanFactoryPostProcessors 用于调用 BeanFactoryPostProcessors 的后置处理器,执行程序员自定义的 BeanFactoryPostProcessors 和 Spring 内部自己定义的。

Spring 在这个环境会先后执行 ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistrypostProcessBeanFactory 方法。本篇文章我们就主要分析这两个方法的逻辑。

postProcessBeanDefinitionRegistry

接口回调入口

ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

此方法为接口 BeanDefinitionRegistryPostProcessor 中的方法。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);

    // 将 registryId 放入 registriesPostProcessed 中,在 postProcessBeanFactory 方法中就不会再执行
    this.registriesPostProcessed.add(registryId);

    // 根据方法名,顾名思义,处理配置 Bean
    processConfigBeanDefinitions(registry);
}
处理配置类 Bean 定义

ConfigurationClassPostProcessor#processConfigBeanDefinitions

processConfigBeanDefinitions 方法的作用就是处理配置类,重要部分在 parser.parse(candidates) 这一行。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

    // 存放 appConfig 提供的 BeanDefinition
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

    // 获取容器中注册的所有 BeanDefinition 名字
    String[] candidateNames = registry.getBeanDefinitionNames();

    // Full or Lite
    for (String beanName : candidateNames) {
        // 根据 beanName 拿到 BeanDefinition
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 根据 BeanDefinition 里面的 class 属性来判断
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)||ConfigurationClassUtils.isLiteConfigurationClass(beanDef) {
            // 如果 BeanDefinition 中的 configurationClass 属性为 full 或者 lite,则意味着已经处理过了,直接跳过
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // 判断是不是 Configuration 类,如果加了 @Configuration 注解的就是 Full
            // 如果是下面四个注解就是 Lite
            // candidateIndicators.add(Component.class.getName());
            // candidateIndicators.add(ComponentScan.class.getName());
            // candidateIndicators.add(Import.class.getName());
            // candidateIndicators.add(ImportResource.class.getName());
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    if (configCandidates.isEmpty()) {
        return;
    }

    // 排序,根据 order,不重要
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    SingletonBeanRegistry sbr = null;
    // 如果 BeanDefinitionRegistry 是 SingletonBeanRegistry 子类的话,
    // 由于我们当前传入的 DefaultListableBeanFactory 是 SingletonBeanRegistry 的子类
    // 因此会将 registry 强转为 SingletonBeanRegistry
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            // Bean 名称生成策略器
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            // SingletonBeanRegistry 中有 id 为 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
            // 如果有则利用它的,否则则是 Spring 默认的
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

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

    // 实例化 ConfigurationClassParser 为了解析各个配置类
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // 实例化两个集合,candidates 用于将之前加入的 configCandidates 进行去重,因为可能有多个配置类重复了
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    // alreadyParsed 用于存放是否已经处理过
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
            
    do {
        // 核心解析流程,重要!
        // 其实就是把类上面的特殊注解解析出来,最终封装成 BeanDefinition 注册到工厂
        parser.parse(candidates);
        parser.validate();

        // parser.getConfigurationClasses() 方法拿到所有扫描到和 Import 导入等所有方式导入的类信息
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        // 去除已经解析过的
        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());
        }
        
        // 这里是去真正的注册!刚刚在解析 @Import、@Bean、@Import 等注解的时候,并没实际创建 BeanDefinition 并注册,而是将数据暂存起来
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        // 这样做主要是防止有些 Bean 没有注册
        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
    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();
    }
}
配置类解析 parser.parse

ConfigurationClassParser#parse

我们仔细看一下 parser.parse() 方法,在 ConfigurationClassParser 类中的 parse 方法:

public void parse(Set<BeanDefinitionHolder> configCandidates) {

    this.deferredImportSelectors = new LinkedList<>();

    // 根据 BeanDefinition 的类型做不同的处理,一般都会调用 ConfigurationClassParser#parse 进行解析
    // 此处初始传进来就是一个配置类 appConfig.class
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 扫描注解得到的 BeanDefinition
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                // 非扫描注解得到的 BeanDefinition
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            } else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (Throwable ex) {
            // 抛异常略
        }
    }

    // 处理延迟加载的 importSelect?为什么要延迟加载,估计就是为了延迟吧
	this.deferredImportSelectorHandler.process();
}

protected final void parse(AnnotationMetadata metadata, String beanName) {
    // 根据注解信息和 beanName 创建一个 ConfigurationClass
    // 此方法或被多处递归调用!比较难理解...
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
解析配置类 processConfigurationClass

ConfigurationClassParser#processConfigurationClass

解析配置类 processConfigurationClass 方法,重点!

protected void processConfigurationClass(ConfigurationClass configClass)  {
    // 根据注解信息判断是不是跳过解析
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    // 处理 Imported 的情况,就是当前这个注解类有没有被别的类 import
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            return;
        } else {
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    // 递归地处理配置类及其超类层次结构
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // 处理配置类
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}
真正开始解析 doProcessConfigurationClass

ConfigurationClassParser#doProcessConfigurationClass

整个过程只有 @ComponentScan 进行了 BD 的创建和注册,其他的都没有进行实际性的 BD 创建注册。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass){

    // 首先递归地处理内部类(内部类也可以使用相关注解)
    processMemberClasses(configClass, sourceClass);

    // 处理 @PropertySource 注解,加载外部的 properties 配置文件,通过 environment 加载
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), PropertySources.class,
        org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
    }

    // 处理 @ComponentScan 注解
    // 此处或扫描到 @ComponentScan 注解包下面的所有加了 @Component、@Service 等注解的类
    // 注意的是,每扫描到一个符合条件的类,都要进行递归扫描,重新调用 doProcessConfigurationClass() 方法
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // 扫描解析在这里
            // 这里扫描出来所有 @Component,并且把扫描的出来的普通 bean 放到 bdMap 当中
            Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

            // 检查扫描的定义集是否有任何进一步的配置类,并在需要时进行递归解析
            // 检查扫描出来的类当中是否还有 configuration
            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());
                }
            }
        }
    }
    
    /**
    *  下面这个方法是处理 @Import 注解 导入类的三种情况:
    *  1. ImportSelector
    *  2. 普通类
    *  3. ImportBeanDefinitionRegistrar
    *  此处 import 导入的类并不会立刻转化为 bd 放入到 bdMap 中
    * 
    * 这里处理的 import 是需要判断我们的配置类有 @Import 注解
    * 在解析的过程中如果 @Import 指定是一个普通类,
    * 比如 @Import(Student.class),那么这里便把 Student 类当做 @Configuration 类进行解析(这里就有递归)
    * 虽然这里当做 @Configuration 类进行解析,但是当前 Import 的这个类不会创建 BD 进行注册
    *
    * 在解析的过程中如果发觉是一个 importSelector 实现类
    * 首先反射创建 importSelector 对象
    * 然后执行一系列 Aware 通知方法
    * 然后回调 selector() 方法,返回一个字符串(类名)数组
    * 继而在递归调用本方法来处理这个类
    * 这里不会创建 BD,更不会注册
    *
    * 如果发现是 ImportBeanDefinitionRegistrar 实现类
    * 首先反射创建 ImportBeanDefinitionRegistrar 对象
   	* 然后执行一系列 Aware 通知方法
    * 然后将创建的对象添加到配置类的 importBeanDefinitionRegistrars 中
    * 这样不会递归去处理,也不会创建 BD,更不会注册
    */
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // 处理 @ImportResource 注解
    // 这里只是去解析配置文件的位置添加到配置类的 importedResources 中,没有进行扫描处理
    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);
        }
    }

    // 处理单个的 @Bean 方法,这里也只是进行解析,并没有实际的创建 BD 和注册
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 处理接口上的默认方法
    processInterfaces(configClass, sourceClass);

    // 处理超类(如果有的话)
    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;
}

这里要注意:

  1. 首先把配置类 appConfig.class 类转化后的的 BeanDefinition,并标记为 Full 还是 Lite

  2. 然后第一次进入到parser.parse()方法时,参数只有一个配置类 appConfig.class

  3. 接着调用 parser.parse(appConfig.class) 开始扫描,此处是个循环

  4. 接着递归调用 doProcessConfigurationClass() 方法

    • 在这个方法中,先处理 @PropertySource 注解

    • 接着处理 @ComponentScan 注解,把扫描到的类符合条件的放入到集合中,遍历这个结合,再次调用 parse() 扫描方法。此处扫描出来的类,会被转化为BeanDefinition,然后直接注册到 bdMap

    • 接着处理 @Import 注解,分三种情况,分别是:ImportSelectorImportBeanDefinitionRegistrar、普通导入类。此处会递归调用 processConfigurationClass() 方法来确定被导入的类是否又需要解析。注意,此处导入符合条件的类,并不会立即转化为 BeanDefinition!而是在当前类解析完成后,通过 reader.loadBeanDefinitions(configClasses) 创建和注册 BeanDefinition

    • 接着处理 @ImportResource 注解

    • 接着处理处理单个的 @Bean 方法,不会立即转化为 BeanDefinition!而是在当前类解析完成后,通过 reader.loadBeanDefinitions(configClasses) 创建和注册 BeanDefinition

postProcessBeanFactory

此方法就是为全注解类生成的 CGLib 代理,至于代理类的生成,是通过使用 Enhancer 增强器。

接口回调入口

此方法为接口 BeanFactoryPostProcessor 中的方法。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    int factoryId = System.identityHashCode(beanFactory);
    this.factoriesPostProcessed.add(factoryId);
    // registriesPostProcessed中 已经包含了 factoryId
    if (!this.registriesPostProcessed.contains(factoryId)) {
        processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
    }
	// 根据方法名,顾名思义,增强配置类
    enhanceConfigurationClasses(beanFactory);
    // 向 Spring 容器中添加一个 ImportAwareBeanPostProcessor 后置处理器,主要用于将 @Import 注解的元信息注入给配置类
    // 为什么在这里去添加?因为只有 ConfigurationClassPostProcesser 处理完 @Import 之后,ImportAwareBeanPostProcessor 才有意义
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
增强配置类

只对 Full 类型的配置类进行增强。为其包装代理类。

注意:这里不会实际的创建代理对象!而是先创建代理类的结构,此时的所有处理都是围绕着 @Configuration 代理类的 BeanDefinition。

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    
    // 此 Map 是存放具有 Full 属性的 BeanDefinition
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    
    // 遍历 BeanFactory 工厂中的 BeanDefinitionNames,获取所有 @Configuration 修饰的 BeanDefinition
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        // 根据 beanName 从 bdMap 中拿到对应的 BeanDefinition
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        // 判断 BeanDefinition 是否是 Full 类型
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
            configBeanDefs.put(beanName, (AbstracteanDefinition) beanDef);
        }
    }
    
    if (configBeanDefs.isEmpty()) {
        // nothing to enhance -> return immediately
        return;
    }

    // 创建一个配置类增强器
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    // 循环存放属性为 Full 的 BeanDefinition
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
        
        AbstractBeanDefinition beanDef = entry.getValue();
        // If a @Configuration class gets proxied, always proxy the target class
        beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        try {
            // Set enhanced subclass of the user-specified bean class
            Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
            if (configClass != null) {
                // 完成对全注解类的 CGLib 代理
                Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
                if (configClass != enhancedClass) {
                    // 替换原有的 Class 为代理后的类型
                    beanDef.setBeanClass(enhancedClass);
                }
            }
        } catch (Throwable ex) {
            // 抛异常代码略
        }
    }
}
// ConfigurationClassEnhancer#enhance
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
   // 判断是否被代理过
   if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
      return configClass;
   }
    
   // 没有被代理 CGLib 代理则创建代理
   Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
   return enhancedClass;
}

// 创建 CGLib 动态代理
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
   Enhancer enhancer = new Enhancer();
   // 增强父类,地球人都知道 CGLib 是基于继承来的,不要求父类实现接口
   enhancer.setSuperclass(configSuperClass);
   // 增强接口,为什么要增强接口?便于判断,表示一个类以及被增强了
   enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
   // 不继承 Factory 接口
   enhancer.setUseFactory(false);
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   // BeanFactoryAwareGeneratorStrategy 是一个生成策略
   // 主要为生成的 CGLIB 类中添加成员变量 $$beanFactory
   // 同时基于接口 EnhancedConfiguration 的父接口 BeanFactoryAware 中的 setBeanFactory 方法,
   // 设置此变量的值为当前 Context 中的 beanFactory,这样一来我们这个 CGLIB 代理的对象就有了 beanFactory    	
   // 有了 factory 就能获得对象,而不用去通过方法获得对象了,因为通过方法获得对象不能控制器过程
   // 该 BeanFactory 的作用是在 this 调用时拦截该调用,并直接在 beanFactory 中获得目标 bean
   enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
   // 过滤方法,不能每次都去 new
   enhancer.setCallbackFilter(CALLBACK_FILTER);
   enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
   return enhancer;
}

这里增强的内容由两个拦截器提供,这两个类都是 ConfigurationClassEnhancer 的内部类:

  1. BeanMethodInterceptor:用于为 @Bean 方法添加作用域的支持和 BeanFactory 支持,以及解析 Bean 的依赖关系(@Bean 方法的参数注入)。
  2. BeanFactoryAwareMethodInterceptor:用于注入相关的 Aware 对象。

为什么要创建 CGLib 去增强?

原因是为了确保 @Configuration 类和 @Bean 方法能够按照预期的方式工作。通过代理和其他技术手段,Spring 可以确保 bean 的单例性、依赖注入、懒加载等特性都能得到正确处理,从而保证整个应用的配置和运行都符合 Spring 框架的设计规范和预期行为。

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    @Bean
    public MyRepository myRepository() {
        // myRepository 依赖 myService
        return new MyRepositoryImpl(myService());
    }
}

在这个示例中,myRepository 依赖 myService。如果没有增强,调用 myService() 方法时,每次都会返回一个新的 MyServiceImpl 实例,而不是返回同一个单例实例。通过增强配置类,Spring 能够确保 myService() 方法返回的始终是同一个实例。

为什么使用的是 CGLib 去做动态代理而不是 JDK?

由于这些类通常不会实现任何接口,因此使用 CGLIB 动态代理更为合适。

详细步骤解析

配置类扫描:@ComponentScan 注解的解析

@ComponentScan 只会扫描 @Component 和 @Component 的衍生注解,例如 @Controller、@Service、@Repository 和没有再启动时指定的 @Configuration 注解,像 @Bean、@Import 等注解不在这里扫描。

通过上面的源码分析,我们得知,ConfigurationClassParser#doProcessConfigurationClass 在真正进行 @ComponentScan 处理的时候,将扫描和生成注册 BeanDefinition 的职责转交给 ComponentScanAnnotationParser 类处理。

// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

if (!componentScans.isEmpty() &&
    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    for (AnnotationAttributes componentScan : componentScans) {
        // 扫描、创建 BD、注册 BD,都是由 ComponentScanAnnotationParser 处理
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        
        // 检查扫描结果,继续递归进行扫描注册
        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());
            }
        }
    }
}

接下来我们详细看一下 ComponentScanAnnotationParser 的 parse 方法。

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // Spring 自己 new 的一个 ClassPathBeanDefinitionScanner 进行扫描!这个类见到很多次了
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    // 下面这一堆都是在配置扫描策略...
    
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    // 设置扫描器的 BeanName 生成器
    scanner.setBeanNameGenerator(useInheritedGenerator ?
                                 this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass));

    // 设置 componentScan 的代理模式
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
        scanner.setScopedProxyMode(scopedProxyMode);
    } else {
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
        scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    // 设置扫描器的资源模式
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    // 看看 componentScan 注解是否设置了 includeFilters 属性
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addIncludeFilter(typeFilter);
        }
    }

    // 看看 componentScan 注解是否设置了 excludeFilters 属性
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addExcludeFilter(typeFilter);
        }
    }

    // 	看看 componentScan 注解是否设置了 lazyInit 属性
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    // 此集合放的是扫描的路径
    Set<String> basePackages = new LinkedHashSet<>();
    // 获取componentScan注解的basePackages属性
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                                                               ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    // 开始扫描
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

这里就是 ClassPathBeanDefinitionScanner 发挥作用的地方,用于扫描、注册指定包下的所有 Bean(创建 BeanDefinition 并注册到上下文)。这里传入的是可变参数是 Spring 对于多个扫描包情况的考虑。具体逻辑可以查看 ClassPathBeanDefinitionScanner 的 doScan 方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 存储创建出的 BeanDefinitionHolder
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 依次扫描每个包中的相关注解,创建获取到所有的 BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成 Bean 的名称
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                // 包装成 BeanDefinitionHolder
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 创建代理
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                 // 注册到上下文工厂
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

扫描的核心是 findCandidateComponents 方法,这个方法就是寻找到 @Component 和 @Component 的衍生注解的类。

关于 ClassPathBeanDefinitionScanner 的详解,可以查看文章:《深入解析Spring BeanDefinition:理解加载、解析与注册的全过程

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值