BeanFactoryPostProcessor 容器初始化的底层原理

132 阅读25分钟

引言

Spring框架的魅力在于其强大的扩展机制,而BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor作为最底层的扩展点,直接掌控着Bean定义的解析、注册与修改流程。本文将结合完整的模拟Spring容器初始化案例,深入剖析这两个后处理器的底层实现原理,揭示Spring如何通过它们完成从类扫描、配置解析到Bean注册的生命周期管理。

一、Spring容器的核心架构:BeanFactory与BeanDefinitionRegistry

要理解后处理器的作用,首先需要明确Spring容器的核心组件:BeanFactoryBeanDefinitionRegistry

1.1 BeanFactory:Bean的“工厂”

BeanFactory是Spring容器的核心接口,负责管理Bean的生命周期。其默认实现DefaultListableBeanFactory包含以下关键组件:

  • BeanDefinitionMap​:存储所有Bean的定义(BeanDefinition对象),记录Bean的类型、作用域、依赖关系等元数据。
  • BeanDefinitionRegistry​:提供注册、删除、查询Bean定义的接口(DefaultListableBeanFactory实现了此接口)。
  • 后处理器集合​:存储BeanPostProcessorBeanFactoryPostProcessor等扩展点,用于干预Bean的创建过程。

1.2 BeanDefinition:Bean的“设计图”

BeanDefinition是Bean的元数据描述,包含以下核心信息:

  • beanClass:Bean的Java类型(如BeanImitateA.class)。
  • scope:作用域(singletonprototype等)。
  • constructorArguments:构造方法参数。
  • initMethodName/destroyMethodName:初始化/销毁方法名。
  • factoryBeanName/factoryMethodName:工厂Bean名称和工厂方法名(用于@Bean方法)。

关键结论​:Spring容器的本质是“根据BeanDefinition创建和管理Bean实例的工厂”,而后处理器的作用是在这个过程中动态修改或扩展BeanDefinition


二、BeanFactoryPostProcessor:修改Bean定义的“编辑器”

2.1 执行时机:Bean定义解析完成后

BeanFactoryPostProcessor的执行时机在refresh()方法的invokeBeanFactoryPostProcessors(beanFactory)阶段,此时:

  • 所有BeanDefinition已被加载到BeanDefinitionRegistry(如@Component@Bean注解的类已被解析)。
  • 但尚未实例化任何Bean(实例化发生在finishBeanFactoryInitialization阶段)。

2.2 核心职责:修改现有BeanDefinition

BeanFactoryPostProcessor允许开发者:

  • 解析自定义注解(如@MyConfiguration)并生成额外的BeanDefinition
  • 修改现有BeanDefinition的属性(如作用域、依赖关系)。
  • 动态调整Bean的作用域(如将单例改为原型)。

2.3 源码级实现:ConfigurationClassPostProcessor的启示

Spring内置的ConfigurationClassPostProcessor是最典型的BeanFactoryPostProcessor,其核心逻辑如下(简化自Spring源码):

public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 1. 扫描所有被@Configuration标记的类(配置类)
        Set<BeanDefinition> configCandidates = beanFactory.getBeanDefinitions().stream()
            .filter(def -> ClassUtils.isPresent(def.getBeanClassName(), beanFactory.getBeanClassLoader()))
            .filter(def -> ConfigurationClassUtils.checkConfigurationClassCandidate(def, beanFactory.getConfigurationAnnotations()))
            .collect(Collectors.toSet());

        // 2. 解析配置类中的@Bean方法、@ComponentScan等注解
        for (BeanDefinition configDef : configCandidates) {
            String configClassName = configDef.getBeanClassName();
            try {
                // 加载配置类元数据
                Class<?> configClass = beanFactory.getBeanClassLoader().loadClass(configClassName);
                ConfigurationClass config = new ConfigurationClass(configClass, configClassName);
                
                // 处理@ComponentScan注解(触发组件扫描)
                processComponentScan(config, beanFactory);
                
                // 处理@Bean方法(生成BeanDefinition)
                processBeanMethods(config, beanFactory);
            } catch (ClassNotFoundException e) {
                throw new BeanDefinitionStoreException("无法加载配置类:" + configClassName, e);
            }
        }
    }

    private void processComponentScan(ConfigurationClass config, ConfigurableListableBeanFactory beanFactory) {
        // 解析@ComponentScan的basePackages,触发组件扫描逻辑(类似本文的ComponentScanPostProcessorCase)
    }

    private void processBeanMethods(ConfigurationClass config, ConfigurableListableBeanFactory beanFactory) {
        // 解析@Bean方法,生成BeanDefinition(类似本文的BeanPostProcessorCase)
    }
}

关键逻辑​:ConfigurationClassPostProcessor通过解析配置类的元数据,将@Bean方法转换为BeanDefinition,并将@ComponentScan路径转换为组件扫描任务。这一过程为后续的组件扫描和@Bean方法注册奠定了基础。


三、BeanDefinitionRegistryPostProcessor:动态注册Bean的“工厂”

3.1 执行时机:Bean定义注册阶段

BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子接口,额外提供了postProcessBeanDefinitionRegistry方法。其执行时机在BeanFactoryPostProcessor之前(更早介入Bean定义阶段),允许:

  • 动态注册新的BeanDefinition(如手动扫描的组件、Mapper接口)。
  • 修改或删除已存在的BeanDefinition(需谨慎使用)。

3.2 核心职责:动态扩展Bean定义

BeanFactoryPostProcessor只能修改现有BeanDefinition不同,BeanDefinitionRegistryPostProcessor可以直接操作BeanDefinitionRegistry,实现:

  • 手动注册第三方库的Bean(如MyBatis Mapper)。
  • 动态生成配置类(如根据数据库表生成DAO)。
  • 替换默认的Bean定义(如覆盖DataSource的默认配置)。

3.3 源码级实现:组件扫描的底层逻辑

以本文的ComponentScanPostProcessorCase为例,模拟@ComponentScan的底层实现:

/**
     * 手动模拟 @ComponentScan 注解的组件扫描逻辑
     * <p>
     * Spring @ComponentScan 核心实现:
     * 1. ComponentScanAnnotationParser 解析 @ComponentScan 注解的参数(basePackages、includeFilters 等)
     * 2. ClassPathBeanDefinitionScanner 执行扫描(基于解析的参数)
     * 3. 扫描过程中,使用 ClassMetadata 判断类是否符合条件(如是否被 @Component 标记)
     * 4. 符合条件的类会被封装为 BeanDefinition,并注册到 BeanDefinitionRegistry
     */
    static class ComponentScanPostProcessorCase implements BeanDefinitionRegistryPostProcessor {


        /**
         * 在 Bean 定义注册阶段,模拟 @ComponentScan 扫描组件并注册 BeanDefinition
         * <p>
         * 关键底层步骤:
         * 1. 解析 @ComponentScan 注解:获取 basePackages、includeFilters、excludeFilters 等参数
         * 2. 资源定位:根据 basePackages 生成类路径资源模式 (如 classpath*:com/xxx/*.class)
         * 3. 资源扫描:使用 PathMatchingResourcePatternResolver 扫描所有匹配的资源
         * 4. 元数据读取:使用 CachingMetadataReaderFactory 读取每个资源的类元数据
         * 5. 组件过滤:通过 AnnotationMetadata 判断类是否被 @Component 或其派生注解标记
         * 6. BeanDefinition 构建:为符合条件的类生成 BeanDefinition(默认作用域为单例)
         * 7. Bean 名称生成:使用 AnnotationBeanNameGenerator 生成(类名首字母小写,或 @Component(name=...) 指定)
         * 8. 注册 BeanDefinition:将生成的 BeanDefinition 存入 BeanDefinitionRegistry
         *
         * @param beanFactory Bean 定义注册中心
         */
        @Override // context.refresh();
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {

            log.info("[ComponentScanPostProcessorCase] 开始执行组件扫描...");

            try {

                // 1. 定位被 @ComponentScan 标记的配置类(示例:ImitateConfig)
                Class<?> configClass = ImitateConfig.class;
                log.info("[ComponentScanPostProcessorCase] 处理配置类:{}", configClass.getName());

                // 2. 解析 @ComponentScan 注解的 basePackages(示例:com.dwl.bean_factory_post_processor.imitate)
                ComponentScan componentScan = AnnotationUtils.findAnnotation(configClass, ComponentScan.class);
                if (componentScan == null || componentScan.basePackages().length == 0) {
                    log.warn("[ComponentScanPostProcessorCase] 未找到 @ComponentScan 注解或 basePackages 配置");
                    return;
                }

                String[] basePackages = componentScan.basePackages();
                log.info("[ComponentScanPostProcessorCase] 扫描包路径:{}", String.join(",", basePackages));

                // 3. 初始化类路径扫描器(使用 AnnotationBeanNameGenerator 生成 Bean 名称)
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

                // 4. 遍历每个包路径,扫描类文件
                for (String basePackage : basePackages) {
                    String resourcePattern = "classpath*:" + basePackage.replace('.', '/') + "/**/*.class";
                    log.info("[ComponentScanPostProcessorCase] 扫描包 {} 对应的资源模式:{}", basePackage, resourcePattern);

                    // 5. 扫描类路径资源
                    Resource[] resources = new PathMatchingResourcePatternResolver()
                            .getResources(resourcePattern);
                    log.info("[ComponentScanPostProcessorCase] 包 {} 扫描到 {} 个类文件资源", basePackage, resources.length);

                    // 6. 检查每个类是否为组件(@Component 或其派生注解)
                    for (Resource resource : resources) {
                        MetadataReader reader = factory.getMetadataReader(resource);
                        ClassMetadata classMetadata = reader.getClassMetadata();
                        String className = classMetadata.getClassName();

                        // 6.1 判断是否包含 @Component 或其派生注解(如 @Service、@Repository)
                        // 注:@Component 是根注解,@Service 等是其派生注解(通过 @Component 元注解)
                        boolean isComponent = reader.getAnnotationMetadata()
                                .hasAnnotation(Component.class.getName())
                                || reader.getAnnotationMetadata()
                                .hasMetaAnnotation(Component.class.getName()); // 检查元注解
                        if (!isComponent) {
                            log.info("[ComponentScanPostProcessorCase] 跳过非组件类:{}", className);
                            continue;
                        }

                        // 7. 生成 BeanDefinition(默认作用域为单例,无作用域注解时)
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(classMetadata.getClassName())
                                .getBeanDefinition();
                        log.info("[ComponentScanPostProcessorCase] 生成 BeanDefinition:{}", className);

                        // 8. 生成 Bean 名称(默认规则:类名首字母小写)
                        String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
                        log.info("[ComponentScanPostProcessorCase] 生成 Bean 名称:{}(对应类:{})", beanName, className);

                        // 9. 注册 BeanDefinition(核心操作)
                        beanFactory.registerBeanDefinition(beanName, beanDefinition);
                        log.info("[ComponentScanPostProcessorCase] 注册组件 Bean:{}", beanName);
                    }

                }

            } catch (Exception e) {
                throw new RuntimeException("组件扫描或注册失败", e);
            }
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            // 无需实现(当前场景无需修改 BeanFactory)
        }
    }

关键步骤解析​:

  • 资源加载​:PathMatchingResourcePatternResolver负责将包路径转换为类路径资源模式(如classpath*:com/dwl/.../**/*.class),并加载所有匹配的.class文件。
  • 元数据读取​:CachingMetadataReaderFactory通过缓存类元数据(如类的注解、方法、字段)避免重复IO,提升性能。
  • 组件过滤​:通过ClassMetadataAnnotationMetadata判断类是否被@Component或其派生注解(如@Service)标记。
  • Bean定义生成​:使用BeanDefinitionBuilder构建BeanDefinition,默认作用域为单例(SCOPE_SINGLETON)。
  • Bean名称生成​:AnnotationBeanNameGenerator根据类名生成默认Bean名称(首字母小写),或使用@Component(name="xxx")指定的名称。

四、@Bean方法的解析与注册:ConfigurationClassPostProcessor的深层逻辑

4.1 @Bean方法的底层处理流程

Spring处理@Bean方法的流程由ConfigurationClassPostProcessor主导,核心步骤如下(结合本文BeanPostProcessorCase案例):

4.1.1 识别配置类

@Configuration@Component标记的类会被识别为配置类。配置类的特点是:

  • 可以包含@Bean方法。
  • 支持方法级依赖注入(@Bean方法之间可相互调用,返回单例)。
  • 支持@PostConstructInitializingBean等生命周期回调。

4.1.2 解析@Bean方法

通过MetadataReader读取配置类的元数据,获取所有被@Bean标记的方法:

// 伪代码:解析配置类的@Bean方法
Set<MethodMetadata> beanMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());

4.1.3 生成BeanDefinition

为每个@Bean方法生成BeanDefinition,关键属性包括:

  • beanClass:方法的返回类型(如BeanImitateA.class)。
  • factoryBeanName:配置类的名称(如"beanImitateConfig")。
  • factoryMethodName@Bean方法的名称(如"beanImitateA")。
  • scope:方法上的@Scope注解值(默认singleton)。
  • initMethodName:方法上的@Bean(initMethod="xxx")指定的初始化方法。

示例​:对于BeanImitateConfig中的beanImitateA()方法,生成的BeanDefinition如下:

// 4.1 构建 BeanDefinition(工厂方法为当前 @Bean 方法,工厂 Bean 为配置类)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
    .genericBeanDefinition()
    .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR)
    .setFactoryMethodOnBean(method.getMethodName(), "beanImitateConfig") // 工厂方法:配置类的 @Bean 方法
    .getBeanDefinition();

4.1.4 注册BeanDefinition

将生成的BeanDefinition注册到BeanDefinitionRegistry,后续容器刷新时会实例化这些Bean。


五、Mapper接口的手动注册:MyBatis与Spring整合的底层原理

5.1 MapperFactoryBean的作用

MyBatis与Spring整合的核心是将Mapper接口绑定到SqlSessionFactory,这一过程通过MapperFactoryBean实现。MapperFactoryBeanFactoryBean的实现类,负责:

  1. 获取SqlSessionFactory(通过依赖注入)。
  2. 为Mapper接口生成代理对象(JDK动态代理或CGLIB)。
  3. 代理对象将方法调用转发给SqlSession执行SQL。

5.2 自定义后处理器模拟Mapper扫描

本文的MapperPostProcessorCase模拟了MyBatis的MapperScannerConfigurer,核心逻辑如下:

5.2.1 扫描Mapper包

通过ClassPathResourcePatternResolver加载指定包路径下的所有类文件:

String basePackage = "com.dwl.bean_factory_post_processor.mapper_imitate.mapper";
String resourcePattern = "classpath*:" + basePackage.replace('.', '/') + "/**/*.class";
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(resourcePattern);

5.2.2 过滤Mapper接口

通过ClassMetadata判断类是否为接口:

ClassMetadata classMetadata = reader.getClassMetadata();
if (!classMetadata.isInterface()) {
    continue; // 跳过非接口类
}

5.2.3 生成MapperFactoryBean的BeanDefinition

为每个Mapper接口生成MapperFactoryBeanBeanDefinition,并注册到容器:

// 5. 构建 MapperFactoryBean 的 BeanDefinition
AbstractBeanDefinition mapperBeanDefinition = BeanDefinitionBuilder
            .genericBeanDefinition(MapperFactoryBean.class)
            .addConstructorArgValue(className) // 构造参数:Mapper 接口类(用于生成代理)
            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) // 自动注入 SqlSessionFactory
            .getBeanDefinition();

// 6. 生成 Bean 名称(默认规则:接口名首字母小写)
String beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
log.info("[MapperPostProcessorCase] 生成 Bean 名称:{}(对应接口:{})", beanName, className);

// 7. 注册 BeanDefinition(核心操作:将 BeanDefinition 存入 BeanDefinitionRegistry)
registry.registerBeanDefinition(beanName, mapperBeanDefinition);
log.info("[MapperPostProcessorCase] 成功注册 Mapper Bean:{} -> {}", beanName, className);

5.2.4 容器刷新时的代理生成

当容器实例化MapperFactoryBean时,会调用其getObject()方法生成Mapper代理:

public class MapperFactoryBean<T> implements FactoryBean<T> {
    private final Class<T> mapperInterface;

    @Override
    public T getObject() {
        return sqlSessionFactory.getConfiguration().getMapper(mapperInterface, this);
    }

    // ... 其他方法(设置SqlSessionFactory等)
}

关键结论​:通过自定义后处理器手动注册Mapper接口,本质上是模拟了MyBatis的MapperScannerConfigurer,将Mapper接口转换为FactoryBean,最终生成代理对象。


六、实战验证:完整容器初始化流程

通过本文开头的BeanFactoryPostProcessorCase主方法,我们可以验证各后处理器的执行效果:

6.1 场景1:基础容器 + Mapper扫描

// 注册核心后处理器:负责解析 @Configuration、@ComponentScan、@Bean 等注解
context.registerBean(ConfigurationClassPostProcessor.class);
log.info("[场景1] 4. 注册 ConfigurationClassPostProcessor → 后处理器列表新增该实例");

// 手动注册 Mapper 扫描器(模拟 Spring Boot 自动配置的 MapperScannerConfigurer)
context.registerBean(MapperScannerConfigurer.class,
        db -> db.getPropertyValues().add("basePackage", "com.dwl.bean_factory_post_processor.convention.mapper"));
log.info("[场景1] 5. 注册 MapperScannerConfigurer → 用于扫描 @Mapper 接口并注册 MapperFactoryBean");

// 刷新容器(触发 Bean 生命周期全流程)
log.info("[场景1] 开始刷新容器...");
context.refresh(); // 核心方法:触发 BeanFactory 初始化、后处理器执行、Bean 实例化等

6.2 场景2:自定义组件扫描

log.info("\n===== 场景 2:自定义组件扫描后处理器(模拟 @ComponentScan) =====");
GenericApplicationContext context2 = new GenericApplicationContext();
context2.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类的核心后处理器
context2.registerBean(ComponentScanPostProcessorCase.class); // 自定义组件扫描逻辑
context2.refresh();

6.3 场景3:手动注册@Bean方法

log.info("\n===== 场景 3:手动注册 @Bean 方法定义的 Bean(模拟 @Bean 注解处理) =====");
GenericApplicationContext context3 = new GenericApplicationContext();
context3.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类
context3.registerBean("beanImitateConfig", BeanImitateConfig.class); // 包含 @Bean 方法的配置类
context3.registerBean(BeanPostProcessorCase.class); // 自定义 @Bean 方法解析后处理器
context3.refresh();

6.4 场景4:手动注册Mapper接口

log.info("\n===== 场景 4:手动注册 Mapper 接口(模拟 MyBatis 整合) =====");
GenericApplicationContext context4 = new GenericApplicationContext();
context4.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类
context4.registerBean("mapperImitateConfig", MapperImitateConfig.class); // Mapper 配置类
context4.registerBean(MapperPostProcessorCase.class); // 自定义 Mapper 注册后处理器
context4.refresh();

七、总结与扩展

7.1 核心结论

  • BeanFactoryPostProcessor​:在Bean定义解析完成后,修改现有Bean定义(如解析@Configuration中的@Bean方法)。
  • BeanDefinitionRegistryPostProcessor​:在Bean定义注册阶段,动态注册新Bean定义(如组件扫描、Mapper接口注册)。
  • 组件扫描​:通过ClassPathBeanDefinitionScanner实现,核心是资源加载、元数据读取、组件过滤和Bean定义生成。
  • ​@Bean方法解析​:由ConfigurationClassPostProcessor主导,将@Bean方法转换为BeanDefinition,工厂方法指向配置类。
  • Mapper注册​:通过自定义后处理器模拟MapperScannerConfigurer,生成MapperFactoryBean的Bean定义,最终生成代理对象。

7.2 扩展思考

  • 性能优化​:CachingMetadataReaderFactory通过缓存类元数据减少IO,实际开发中可借鉴此模式优化资源加载。
  • 依赖注入​:@Bean方法的参数解析依赖Spring的依赖注入机制,底层通过BeanFactory的单例池实现。
  • 作用域管理​:Bean的作用域(单例、原型等)在BeanDefinition中定义,实例化阶段由Scope接口实现。
  • 自定义注解驱动​:通过自定义后处理器,可实现类似@Autowired的依赖注入、@PostConstruct的初始化回调等功能。

完整代码

package com.dwl.bean_factory_post_processor;

import com.dwl.bean_factory_post_processor.bean_imitate.BeanImitateConfig;
import com.dwl.bean_factory_post_processor.convention.ConventionConfig;
import com.dwl.bean_factory_post_processor.imitate.ImitateConfig;
import com.dwl.bean_factory_post_processor.mapper_imitate.MapperImitateConfig;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;
import java.util.Set;

/**
 * @ClassName BeanFactoryPostProcessorCase
 * @Description 演示 Spring 中 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的使用,
 * 以及手动模拟 Spring 核心功能(如组件扫描、@Bean 注册、Mapper 接口绑定)的完整案例。
 * <p>
 * Spring 底层核心流程(贯穿所有场景):
 * 1. 容器初始化阶段:
 * - 创建 GenericApplicationContext → 内部初始化 DefaultListableBeanFactory(核心 BeanFactory 实现)
 * - DefaultListableBeanFactory 初始化时注册大量内置后处理器(如 AutowiredAnnotationBeanPostProcessor)
 * 2. 后处理器注册阶段:
 * - 显式注册 ConfigurationClassPostProcessor(Spring 自动配置的核心后处理器)
 * - 显式注册自定义后处理器(如 ComponentScanPostProcessorCase、BeanPostProcessorCase)
 * 3. 容器刷新阶段(refresh() 方法):
 * a. 准备刷新上下文(初始化环境变量、事件发布器等)
 * b. 初始化 BeanFactory(设置类加载器、属性、内置后处理器)
 * c. 执行 BeanFactoryPostProcessor(修改 BeanDefinition,如解析 @PropertySource)
 * d. 注册 BeanDefinitionRegistryPostProcessor(动态注册 BeanDefinition,如本例中的 Mapper 扫描)
 * e. 实例化所有剩余单例 Bean(按顺序:依赖注入 → 初始化 → 后置处理器)
 * 4. 容器关闭阶段:
 * - 销毁单例 Bean(调用销毁方法)
 * - 关闭 BeanFactory(清理资源)
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
public class BeanFactoryPostProcessorCase {

    /**
     * 主方法:通过 4 个场景演示 Spring 容器的不同初始化方式及底层原理
     */
    public static void main(String[] args) {
        /* 场景 1:基础容器 + 配置类 + Mapper 扫描(模拟 Spring Boot 自动配置) */
        log.info("===== 场景 1:基础容器初始化(含 Mapper 扫描) =====");
        GenericApplicationContext context = new GenericApplicationContext();
        log.info("[场景1] 1. 创建 GenericApplicationContext,内部初始化 DefaultListableBeanFactory");
        log.info("[场景1]    - BeanFactory 类型:{}", context.getBeanFactory().getClass()); // 输出:class org.springframework.beans.factory.support.DefaultListableBeanFactory
        log.info("[场景1] 2. 初始状态:beanDefinitionCount={}, singletonCount={}",
                context.getBeanFactory().getBeanDefinitionCount(), context.getBeanFactory().getSingletonCount()); // 均为 0

        // 注册配置类(会被 ConfigurationClassPostProcessor 处理)
        context.registerBean(ConventionConfig.class);
        log.info("[场景1] 3. 注册 ConventionConfig 配置类 → BeanDefinition 已加入 beanDefinitionMap");

        // 注册核心后处理器:负责解析 @Configuration、@ComponentScan、@Bean 等注解
        context.registerBean(ConfigurationClassPostProcessor.class);
        log.info("[场景1] 4. 注册 ConfigurationClassPostProcessor → 后处理器列表新增该实例");

        // 手动注册 Mapper 扫描器(模拟 Spring Boot 自动配置的 MapperScannerConfigurer)
        context.registerBean(MapperScannerConfigurer.class,
                db -> db.getPropertyValues().add("basePackage", "com.dwl.bean_factory_post_processor.convention.mapper"));
        log.info("[场景1] 5. 注册 MapperScannerConfigurer → 用于扫描 @Mapper 接口并注册 MapperFactoryBean");

        // 刷新容器(触发 Bean 生命周期全流程)
        log.info("[场景1] 开始刷新容器...");
        context.refresh(); // 核心方法:触发 BeanFactory 初始化、后处理器执行、Bean 实例化等
        log.info("[场景1] 容器刷新完成,当前 Bean 定义列表(部分):");
        Arrays.stream(context.getBeanDefinitionNames())
                .filter(name -> name.contains("Convention") || name.contains("Mapper"))
                .forEach(name -> log.info("  - {}", name));
        context.close();
        log.info("[场景1] 容器已关闭,清理所有 Bean 实例和资源");


        /* 场景 2:仅注册 ConfigurationClassPostProcessor 和自定义组件扫描后处理器 */
        log.info("\n===== 场景 2:自定义组件扫描后处理器(模拟 @ComponentScan) =====");
        GenericApplicationContext context2 = new GenericApplicationContext();
        context2.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类的核心后处理器
        context2.registerBean(ComponentScanPostProcessorCase.class); // 自定义组件扫描逻辑
        context2.refresh();
        log.info("[场景2] 容器刷新完成,扫描到的组件 Bean 列表:");
        Arrays.stream(context2.getBeanDefinitionNames())
                .filter(name -> name.startsWith("imitateBean")) // 过滤 ImitateBean 相关 Bean
                .forEach(name -> log.info("  - {}", name));
        context2.close();

        /* 场景 3:手动通过 BeanDefinition 注册 @Bean 方法定义的 Bean */
        log.info("\n===== 场景 3:手动注册 @Bean 方法定义的 Bean(模拟 @Bean 注解处理) =====");
        GenericApplicationContext context3 = new GenericApplicationContext();
        context3.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类
        context3.registerBean("beanImitateConfig", BeanImitateConfig.class); // 包含 @Bean 方法的配置类
        context3.registerBean(BeanPostProcessorCase.class); // 自定义 @Bean 方法解析后处理器
        context3.refresh();
        log.info("[场景3] 容器刷新完成,@Bean 方法注册的 Bean 列表:");
        Arrays.stream(context3.getBeanDefinitionNames())
                .filter(name -> name.startsWith("beanImitate")) // 过滤 beanImitate 相关 Bean
                .forEach(name -> log.info("  - {}", name));
        context3.close();

        /* 场景 4:手动注册 Mapper 接口的 BeanDefinition(模拟 MyBatis Mapper 扫描) */
        log.info("\n===== 场景 4:手动注册 Mapper 接口(模拟 MyBatis 整合) =====");
        GenericApplicationContext context4 = new GenericApplicationContext();
        context4.registerBean(ConfigurationClassPostProcessor.class); // 处理配置类
        context4.registerBean("mapperImitateConfig", MapperImitateConfig.class); // Mapper 配置类
        context4.registerBean(MapperPostProcessorCase.class); // 自定义 Mapper 注册后处理器
        context4.refresh();
        log.info("[场景4] 容器刷新完成,Mapper 接口注册的 Bean 列表:");
        Arrays.stream(context4.getBeanDefinitionNames())
                .filter(name -> name.endsWith("Mapper")) // 过滤 Mapper 相关 Bean
                .forEach(name -> log.info("  - {}", name));
        context4.close();

    }

    /**
     * 手动注册 Mapper 接口的 BeanDefinition(模拟 MyBatis Mapper 扫描逻辑)
     * <p>
     * MyBatis 整合 Spring 的核心原理:
     * 1. MapperScannerConfigurer(实现 BeanDefinitionRegistryPostProcessor)在容器刷新时被触发
     * 2. 扫描指定包下的所有 @Mapper 接口(通过类路径资源扫描)
     * 3. 为每个接口生成 MapperFactoryBean 的 BeanDefinition(MapperFactoryBean 是 Spring 的 FactoryBean 实现)
     * 4. MapperFactoryBean 在实例化时会通过 SqlSessionFactory 创建 Mapper 接口的代理对象(JDK 动态代理或 CGLIB)
     * 5. 代理对象会将方法调用转发给 SqlSession 的对应方法(最终执行 SQL)
     */
    static class MapperPostProcessorCase implements BeanDefinitionRegistryPostProcessor {

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            log.info("[MapperPostProcessorCase] 开始扫描 Mapper 接口...");


            try {
                // 1. 定义扫描路径(示例:com.dwl.bean_factory_post_processor.mapper_imitate.mapper)
                String basePackage = "com.dwl.bean_factory_post_processor.mapper_imitate.mapper";
                String resourcePattern = "classpath*:" + basePackage.replace('.', '/') + "/**/*.class";
                log.info("[MapperPostProcessorCase] 扫描路径转换为资源模式:{}", resourcePattern);

                // 2. 扫描类路径资源(PathMatchingResourcePatternResolver 是 Spring 内置的资源解析器)
                Resource[] resources = new PathMatchingResourcePatternResolver()
                        .getResources(resourcePattern);
                log.info("[MapperPostProcessorCase] 扫描到 {} 个类文件资源", resources.length);

                // 3. 使用 CachingMetadataReaderFactory 缓存类元数据(提升性能,避免重复解析)
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                log.info("[MapperPostProcessorCase] 使用 CachingMetadataReaderFactory 缓存类元数据(默认缓存大小无限制)");

                // 4. 遍历资源,解析类元信息
                for (Resource resource : resources) {

                    // 4.1 读取类元数据(MetadataReader 是 Spring 提供的类元信息读取接口)
                    MetadataReader reader = factory.getMetadataReader(resource);
                    ClassMetadata classMetadata = reader.getClassMetadata();
                    String className = classMetadata.getClassName();
                    log.info("[MapperPostProcessorCase] 处理类:{}", className);

                    // 4.2 过滤条件:必须是接口(@Mapper 接口是接口,而非类)
                    if (!classMetadata.isInterface()) {
                        log.info("[MapperPostProcessorCase] 跳过非接口类:{}", className);
                        continue;
                    }

                    // 4.3 检查是否包含 @Mapper 注解(或通过其他方式判断,如 MyBatis 的 @MapperScan 注解)
                    // 注:实际 MyBatis 中通过 MapperRegistry 扫描 @Mapper 接口,此处简化为直接判断接口
                    log.info("[MapperPostProcessorCase] 发现 @Mapper 接口:{}", className);


                    // 5. 构建 MapperFactoryBean 的 BeanDefinition
                    AbstractBeanDefinition mapperBeanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(className) // 构造参数:Mapper 接口类(用于生成代理)
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) // 自动注入 SqlSessionFactory
                            .getBeanDefinition();

                    // 6. 生成 Bean 名称(默认规则:接口名首字母小写)
                    String beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
                    log.info("[MapperPostProcessorCase] 生成 Bean 名称:{}(对应接口:{})", beanName, className);

                    // 7. 注册 BeanDefinition(核心操作:将 BeanDefinition 存入 BeanDefinitionRegistry)
                    registry.registerBeanDefinition(beanName, mapperBeanDefinition);
                    log.info("[MapperPostProcessorCase] 成功注册 Mapper Bean:{} -> {}", beanName, className);

                }

            } catch (Exception e) {
                throw new RuntimeException("扫描或注册 Mapper 接口失败", e);
            }
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            // 无需实现(当前场景仅需注册 BeanDefinition,无需修改 BeanFactory)
        }

    }


    /**
     * 手动注册 @Bean 方法定义的 Bean(模拟 @Bean 注解的处理逻辑)
     * <p>
     * Spring 处理 @Bean 的核心流程:
     * 1. ConfigurationClassPostProcessor 扫描所有 @Configuration 配置类
     * 2. 对每个配置类,解析其 @Bean 方法(通过反射或元数据读取)
     * 3. 为每个 @Bean 方法生成对应的 BeanDefinition(工厂方法为 @Bean 方法,工厂 Bean 为配置类本身)
     * 4. 将生成的 BeanDefinition 注册到 BeanDefinitionRegistry
     * 5. 容器刷新时,实例化这些 Bean(调用配置类的 @Bean 方法,依赖注入参数)
     */
    static class BeanPostProcessorCase implements BeanDefinitionRegistryPostProcessor {

        /**
         * 在 Bean 定义注册阶段,手动解析 @Bean 方法并注册 BeanDefinition
         * <p>
         * 关键底层步骤:
         * 1. 定位配置类:通过类路径或显式注册的配置类(如 BeanImitateConfig)
         * 2. 读取类元数据:使用 MetadataReader 读取配置类的类信息
         * 3. 解析 @Bean 方法:通过 AnnotationMetadata 获取所有被 @Bean 标记的方法
         * 4. 构建 BeanDefinition:为每个 @Bean 方法生成对应的 BeanDefinition
         * 5. 注册 BeanDefinition:将 BeanDefinition 存入容器
         *
         * @param beanFactory Bean 定义注册中心
         */
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {

            log.info("[BeanPostProcessorCase] 开始解析 @Bean 方法...");

            try {

                // 1. 定位配置类(示例:BeanImitateConfig)
                Class<?> configClass = BeanImitateConfig.class;
                log.info("[BeanPostProcessorCase] 处理配置类:{}", configClass.getName());

                // 2. 读取类元数据(使用 CachingMetadataReaderFactory 缓存元数据)
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                MetadataReader reader = factory.getMetadataReader(new ClassPathResource(
                        configClass.getName().replace('.', '/') + ".class"));
                ClassMetadata classMetadata = reader.getClassMetadata();
                log.info("[BeanPostProcessorCase] 配置类元数据:类名={}, 是否接口={}",
                        classMetadata.getClassName(), classMetadata.isInterface());

                // 3. 获取所有 @Bean 方法的元信息(@Bean 注解的全限定名)
                Set<MethodMetadata> beanMethods = reader.getAnnotationMetadata()
                        .getAnnotatedMethods(Bean.class.getName());
                log.info("[BeanPostProcessorCase] 发现 {} 个 @Bean 方法", beanMethods.size());

                // 4. 遍历 @Bean 方法,生成并注册 BeanDefinition
                for (MethodMetadata method : beanMethods) {
                    log.info("[BeanPostProcessorCase] 处理 @Bean 方法:{}", method.getMethodName());

                    // 4.1 构建 BeanDefinition(工厂方法为当前 @Bean 方法,工厂 Bean 为配置类)
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition()
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR)
                            .setFactoryMethodOnBean(method.getMethodName(), "beanImitateConfig") // 工厂方法:配置类的 @Bean 方法
                            .getBeanDefinition();

                    // 4.2 处理初始化方法(@Bean(initMethod = "xxx"))
                    String initMethod = Objects.requireNonNull(method.getAnnotationAttributes(Bean.class.getName()))
                            .get("initMethod").toString();
                    if (!initMethod.isEmpty()) {
                        beanDefinition.setInitMethodName(initMethod);
                        log.info("[BeanPostProcessorCase] 设置初始化方法:{}", initMethod);
                    }

                    // 4.3 注册 BeanDefinition(Bean 名称为方法名)
                    beanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
                    log.info("[BeanPostProcessorCase] 注册 @Bean 方法定义的 Bean:{}", method.getMethodName());
                }
            } catch (Exception e) {
                throw new RuntimeException("解析或注册 @Bean 方法失败", e);
            }
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            // 无需实现(当前场景无需修改 BeanFactory)
        }
    }

    /**
     * 手动模拟 @ComponentScan 注解的组件扫描逻辑
     * <p>
     * Spring @ComponentScan 核心实现:
     * 1. ComponentScanAnnotationParser 解析 @ComponentScan 注解的参数(basePackages、includeFilters 等)
     * 2. ClassPathBeanDefinitionScanner 执行扫描(基于解析的参数)
     * 3. 扫描过程中,使用 ClassMetadata 判断类是否符合条件(如是否被 @Component 标记)
     * 4. 符合条件的类会被封装为 BeanDefinition,并注册到 BeanDefinitionRegistry
     */
    static class ComponentScanPostProcessorCase implements BeanDefinitionRegistryPostProcessor {


        /**
         * 在 Bean 定义注册阶段,模拟 @ComponentScan 扫描组件并注册 BeanDefinition
         * <p>
         * 关键底层步骤:
         * 1. 解析 @ComponentScan 注解:获取 basePackages、includeFilters、excludeFilters 等参数
         * 2. 资源定位:根据 basePackages 生成类路径资源模式 (如 classpath*:com/xxx/*.class)
         * 3. 资源扫描:使用 PathMatchingResourcePatternResolver 扫描所有匹配的资源
         * 4. 元数据读取:使用 CachingMetadataReaderFactory 读取每个资源的类元数据
         * 5. 组件过滤:通过 AnnotationMetadata 判断类是否被 @Component 或其派生注解标记
         * 6. BeanDefinition 构建:为符合条件的类生成 BeanDefinition(默认作用域为单例)
         * 7. Bean 名称生成:使用 AnnotationBeanNameGenerator 生成(类名首字母小写,或 @Component(name=...) 指定)
         * 8. 注册 BeanDefinition:将生成的 BeanDefinition 存入 BeanDefinitionRegistry
         *
         * @param beanFactory Bean 定义注册中心
         */
        @Override // context.refresh();
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {

            log.info("[ComponentScanPostProcessorCase] 开始执行组件扫描...");

            try {

                // 1. 定位被 @ComponentScan 标记的配置类(示例:ImitateConfig)
                Class<?> configClass = ImitateConfig.class;
                log.info("[ComponentScanPostProcessorCase] 处理配置类:{}", configClass.getName());

                // 2. 解析 @ComponentScan 注解的 basePackages(示例:com.dwl.bean_factory_post_processor.imitate)
                ComponentScan componentScan = AnnotationUtils.findAnnotation(configClass, ComponentScan.class);
                if (componentScan == null || componentScan.basePackages().length == 0) {
                    log.warn("[ComponentScanPostProcessorCase] 未找到 @ComponentScan 注解或 basePackages 配置");
                    return;
                }

                String[] basePackages = componentScan.basePackages();
                log.info("[ComponentScanPostProcessorCase] 扫描包路径:{}", String.join(",", basePackages));

                // 3. 初始化类路径扫描器(使用 AnnotationBeanNameGenerator 生成 Bean 名称)
                CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

                // 4. 遍历每个包路径,扫描类文件
                for (String basePackage : basePackages) {
                    String resourcePattern = "classpath*:" + basePackage.replace('.', '/') + "/**/*.class";
                    log.info("[ComponentScanPostProcessorCase] 扫描包 {} 对应的资源模式:{}", basePackage, resourcePattern);

                    // 5. 扫描类路径资源
                    Resource[] resources = new PathMatchingResourcePatternResolver()
                            .getResources(resourcePattern);
                    log.info("[ComponentScanPostProcessorCase] 包 {} 扫描到 {} 个类文件资源", basePackage, resources.length);

                    // 6. 检查每个类是否为组件(@Component 或其派生注解)
                    for (Resource resource : resources) {
                        MetadataReader reader = factory.getMetadataReader(resource);
                        ClassMetadata classMetadata = reader.getClassMetadata();
                        String className = classMetadata.getClassName();

                        // 6.1 判断是否包含 @Component 或其派生注解(如 @Service、@Repository)
                        // 注:@Component 是根注解,@Service 等是其派生注解(通过 @Component 元注解)
                        boolean isComponent = reader.getAnnotationMetadata()
                                .hasAnnotation(Component.class.getName())
                                || reader.getAnnotationMetadata()
                                .hasMetaAnnotation(Component.class.getName()); // 检查元注解
                        if (!isComponent) {
                            log.info("[ComponentScanPostProcessorCase] 跳过非组件类:{}", className);
                            continue;
                        }

                        // 7. 生成 BeanDefinition(默认作用域为单例,无作用域注解时)
                        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(classMetadata.getClassName())
                                .getBeanDefinition();
                        log.info("[ComponentScanPostProcessorCase] 生成 BeanDefinition:{}", className);

                        // 8. 生成 Bean 名称(默认规则:类名首字母小写)
                        String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
                        log.info("[ComponentScanPostProcessorCase] 生成 Bean 名称:{}(对应类:{})", beanName, className);

                        // 9. 注册 BeanDefinition(核心操作)
                        beanFactory.registerBeanDefinition(beanName, beanDefinition);
                        log.info("[ComponentScanPostProcessorCase] 注册组件 Bean:{}", beanName);
                    }

                }

            } catch (Exception e) {
                throw new RuntimeException("组件扫描或注册失败", e);
            }
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            // 无需实现(当前场景无需修改 BeanFactory)
        }
    }
}


package com.dwl.bean_factory_post_processor.bean_imitate;

import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName ImitateBeanA
 * @Description 普通 Java 类(未被 @Component 标记),但通过 BeanImitateConfig 的 @Bean 方法注册为 Bean
 * <p>
 * Spring 中 Bean 的生命周期(以本类为例):
 * 1. 实例化(Instantiation):调用无参构造方法创建对象(BeanImitateA())
 * 2. 属性赋值(Populate Properties):无依赖注入(本类无字段)
 * 3. 初始化(Initialization):
 *      a. @PostConstruct 注解方法(本类未使用)
 *      b. 实现 InitializingBean 接口的 afterPropertiesSet() 方法(本类未实现)
 *      c. 自定义初始化方法(本类未定义)
 * 4. 就绪(Ready):Bean 被放入单例池,可供其他 Bean 注入使用
 * 5. 销毁(Destruction):容器关闭时调用(本类未定义销毁方法)
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
public class BeanImitateA {

    public BeanImitateA() {
        log.info("BeanImitateA 构造方法被调用(实例化阶段)");
    }

    // 示例:初始化方法(若通过 @Bean(initMethod = "init") 声明,会在属性赋值后调用)
    public void init() {
        log.info("BeanImitateA.init() 方法被调用(初始化阶段)");
    }
}

package com.dwl.bean_factory_post_processor.bean_imitate;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @ClassName BeanImitateB
 * @Description@Component 标记的类(会被组件扫描自动注册为 Bean)
 * <p>
 * @Component 是 Spring 组件注解的根注解,其派生注解(@Service@Repository@Controller)语义相同,
 * 仅用于区分业务场景。Spring 通过 ClassPathBeanDefinitionScanner 扫描这些注解的类,并注册为 Bean。
 * <p>
 * 底层原理:
 * 1. 组件扫描时,ClassMetadata 判断类是否被 @Component 或其派生注解标记(通过 hasAnnotation 或 hasMetaAnnotation)
 * 2. 符合条件的类会被封装为 BeanDefinition(默认作用域为单例,作用域由 @Scope 注解指定)
 * 3. Bean 名称由 AnnotationBeanNameGenerator 生成(类名首字母小写,或 @Component(name = "xxx") 指定)
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
@Component // 标记为 Spring 组件(会被组件扫描自动发现并注册)
public class BeanImitateB {

    public BeanImitateB() {
        log.info("BeanImitateB 构造方法被调用(实例化阶段)");
    }

    // 示例:@PostConstruct 初始化方法(需 javax.annotation.PostConstruct)
    // @PostConstruct
    // public void init() {
    //     log.info("ImitateBeanB.init() 方法被调用(初始化阶段)");
    // }

}

package com.dwl.bean_factory_post_processor.bean_imitate;

/**
 * @ClassName BeanImitateC
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class BeanImitateC {
}

package com.dwl.bean_factory_post_processor.bean_imitate;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @ClassName BeanImitateConfig
 * @Description 模拟 Spring 中 @Configuration 配置类的作用:
 * 1. 使用 @Component 标记为 Spring 组件(会被组件扫描自动发现)
 * 2. 使用 @ComponentScan 指定需要扫描的包路径(触发组件扫描逻辑)
 * 3. 使用 @Bean 注册自定义 Bean(会被 ConfigurationClassPostProcessor 处理)
 * <p>
 * Spring 配置类底层原理:
 * - 被 @Configuration 标记的类会被解析为 ConfigurationClass(配置类),其与普通类的区别在于:
 * - 包含 @Bean 方法,这些方法会被解析为 BeanDefinition(工厂方法为 @Bean 方法,工厂 Bean 为配置类本身)
 * - 支持方法级依赖注入(@Bean 方法之间可相互调用,Spring 会确保返回单例)
 * - @ComponentScan 注解会触发类路径扫描,将符合条件的组件(@Component 及其派生注解)注册为 Bean
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
@Component // 标记为 Spring 组件(会被组件扫描自动注册)
@ComponentScan("com.dwl.bean_factory_post_processor.bean_imitate") // 指定组件扫描包路径
public class BeanImitateConfig {

    /**
     * @return BeanImitateA 实例(由 Spring 管理生命周期)
     * @Bean 注解标记的方法会被 Spring 处理:
     * 1. 方法返回值类型作为 Bean 的类型(BeanImitateA)
     * 2. 方法名作为 Bean 的名称(默认)或通过 @Bean(name = "xxx") 指定(示例中为 "beanImitateA")
     * 3. 方法参数由 Spring 自动注入(依赖注入,如无参数则直接实例化)
     * 4. 初始化方法通过 @Bean(initMethod = "xxx") 指定(示例中未使用)
     * 5. 销毁方法通过 @Bean(destroyMethod = "xxx") 指定(示例中未使用)
     */
    @Bean // 标记为需要注册的 Bean
    public BeanImitateA beanImitateA() {
        log.info("BeanImitateConfig.beanImitateA() 被调用,创建 BeanImitateA 实例");
        return new BeanImitateA(); // Spring 会将此实例存入单例池(SingletonBeanRegistry)
    }

    /**
     * 未被 @Bean 标记的方法不会被 Spring 处理(仅作为普通方法)
     * 示例:用于演示非 @Bean 方法的行为
     */
    public BeanImitateC beanImitateC() {
        log.info("BeanImitateConfig.beanImitateC() 被调用,创建 BeanImitateC 实例");
        return new BeanImitateC(); // 不会被注册为 Bean(除非手动注册或通过其他方式)
    }

    /**
     * @Bean 方法依赖其他 Bean 的示例:
     * Spring 会自动注入 SqlSessionFactoryBean 依赖的 DataSource(通过方法参数声明)
     * <p>
     * 底层流程:
     * 1. 解析 @Bean 方法的参数类型(DataSource)
     * 2. 在容器中查找匹配的 Bean(单例池中已存在的 DataSource)
     * 3. 将找到的 Bean 注入到方法参数中(依赖注入)
     *
     * @param source 数据源(自动注入)
     * @return SqlSessionFactoryBean 实例(依赖 DataSource)
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource source) {
        log.info("BeanImitateConfig.sqlSessionFactoryBean() 被调用,创建 SqlSessionFactoryBean 实例");
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(source); // 依赖注入 DataSource(由 Spring 自动完成)
        return bean; // SqlSessionFactoryBean 本身也是一个 Bean(工厂 Bean)
    }

    /**
     * 带初始化方法的 @Bean 示例:
     * Spring 会在 Bean 实例化、依赖注入完成后,调用 initMethod 指定的方法
     * <p>
     * 底层流程:
     * 1. 实例化 Bean(调用构造方法)
     * 2. 依赖注入(@Autowired@Resource 等注解处理)
     * 3. 调用初始化方法(@Bean(initMethod = "init") 或 @PostConstruct)
     *
     * @return DruidDataSource 实例(已初始化)
     */
    @Bean(initMethod = "init") // 指定初始化方法
    public DruidDataSource druidDataSource() {
        log.info("BeanImitateConfig.druidDataSource() 被调用,创建 DruidDataSource 实例");
        DruidDataSource source = new DruidDataSource();
        source.setUrl("jdbc:mysql://localhost:3306/spider_flow");
        source.setUsername("root");
        source.setPassword("");
        return source; // Spring 会将其存入单例池,并在容器关闭时调用 close() 方法
    }

}


package com.dwl.bean_factory_post_processor.convention.mapper;

import org.apache.ibatis.annotations.Mapper;

/**
 * @ClassName ConventionBeanMapperA
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Mapper
public interface ConventionBeanMapperA {
}

package com.dwl.bean_factory_post_processor.convention.mapper;

import org.apache.ibatis.annotations.Mapper;

/**
 * @ClassName ConventionBeanMapperB
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Mapper
public interface ConventionBeanMapperB {
}


package com.dwl.bean_factory_post_processor.convention;

/**
 * @ClassName ConventionBean
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class ConventionBean {
}

package com.dwl.bean_factory_post_processor.convention;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @ClassName ConventionConfig
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Component
@ComponentScan("com.dwl.bean_factory_post_processor.convention")
public class ConventionConfig {

    @Bean
    public ConventionBean conventionBean() {
        return new ConventionBean();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource source) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(source);
        return bean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource druidDataSource() {
        DruidDataSource source = new DruidDataSource();
        source.setUrl("jdbc:mysql://localhost:3306/spider_flow");
        source.setUsername("root");
        source.setPassword("");
        return source;
    }
}

package com.dwl.bean_factory_post_processor.imitate;

/**
 * @ClassName ImitateBean
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class ImitateBean {
}

package com.dwl.bean_factory_post_processor.imitate;

import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName ImitateBeanA
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
public class ImitateBeanA {
}


package com.dwl.bean_factory_post_processor.imitate;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @ClassName ImitateBeanB
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Slf4j
@Component
public class ImitateBeanB {

    public ImitateBeanB() {
    }
}



package com.dwl.bean_factory_post_processor.imitate;

import org.springframework.stereotype.Service;

/**
 * @ClassName ImitateBeanC
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Service
public class ImitateBeanC {
}


package com.dwl.bean_factory_post_processor.imitate;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @ClassName ImitateConfig
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */

@Component
@ComponentScan("com.dwl.bean_factory_post_processor.imitate")
public class ImitateConfig {

    @Bean
    public ImitateBean imitateBean() {
        return new ImitateBean();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource source) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(source);
        return bean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource druidDataSource() {
        DruidDataSource source = new DruidDataSource();
        source.setUrl("jdbc:mysql://localhost:3306/spider_flow");
        source.setUsername("root");
        source.setPassword("");
        return source;
    }
}


package com.dwl.bean_factory_post_processor.mapper_imitate.mapper;

import org.apache.ibatis.annotations.Mapper;

/**
 * @ClassName MapperImitateBeanA
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Mapper
public interface MapperImitateBeanA {
}


package com.dwl.bean_factory_post_processor.mapper_imitate.mapper;

import org.apache.ibatis.annotations.Mapper;

/**
 * @ClassName MapperImitateBeanB
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Mapper
public interface MapperImitateBeanB {
}



package com.dwl.bean_factory_post_processor.mapper_imitate;

/**
 * @ClassName ImitateBeanA
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
public class MapperImitateA {
}

package com.dwl.bean_factory_post_processor.mapper_imitate;

import org.springframework.stereotype.Component;

/**
 * @ClassName MapperImitateB
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Component
public class MapperImitateB {
}



package com.dwl.bean_factory_post_processor.mapper_imitate;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @ClassName BeanImitateConfig
 * @Description
 * @Version 1.0.0
 * @Date 2025
 * @Author By Dwl
 */
@Component
@ComponentScan("com.dwl.bean_factory_post_processor.bean_imitate")
public class MapperImitateConfig {


    @Bean
    public MapperImitateA mapperImitateA() {
        return new MapperImitateA();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource source) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(source);
        return bean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource druidDataSource() {
        DruidDataSource source = new DruidDataSource();
        source.setUrl("jdbc:mysql://localhost:3306/spider_flow");
        source.setUsername("root");
        source.setPassword("");
        return source;
    }

    /* 基本方式
     @Bean
    public MapperFactoryBean<MapperImitateBeanA> mapperImitateBeanA(SqlSessionFactory factory) {
        MapperFactoryBean<MapperImitateBeanA> bean = new MapperFactoryBean<>(MapperImitateBeanA.class);
        bean.setSqlSessionFactory(factory);
        return bean;
    }

    @Bean
    public MapperFactoryBean<MapperImitateBeanB> mapperImitateBeanB(SqlSessionFactory factory) {
        MapperFactoryBean<MapperImitateBeanB> bean = new MapperFactoryBean<>(MapperImitateBeanB.class);
        bean.setSqlSessionFactory(factory);
        return bean;
    }

     */


}