BeanFacotry的后置处理器详解

Spring 版本: 6.0.6

BeanFactory 的后置处理器的作用

BeanFactory 的后置处理器(PostProcessor)是用于在 BeanFactory 加载和创建 bean 过程中对 bean 进行进一步处理的接口。它提供了一种机制,可以在 Spring 容器实例化 bean 之前对 bean 进行定制、修改或扩展

两种 BeanFactory 的接口类型

BeanFactoryPostProcessor

可以通过 ConfigurableListableBeanFactoryBeanDefinition 进行修改,这样就会改变 bean 的定义

java 复制代码
@FunctionalInterface
public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor 接口,同时增加了一个接口,该接口提供了 BeanDefinitionRegistry 参数,也就是可以动态注入一些 BeanDefinition, 在 Spring 中典型的应用是 ConfigurationClassPostProcessor,后面会分析这个类

java 复制代码
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

BeanFactory 后置处理器的使用示例

使用示例类说明

  1. CusBeanFactoryPostProcessor 该类实现了 BeanFactoryPostProcessor 接口并且标有 @Component 注解
  2. CusBeanDefinitionRegistryPostProcessor 该类实现了 BeanDefinitionRegistryPostProcessor 接口并且标有 @Component 注解
  3. RegisterComponent 普通类,会通过 CusBeanDefinitionRegistryPostProcessor 注入到 Spring 容器
  4. ScanComponent 标有 @Component@Scope("prototype"), 会通过 CusBeanFactoryPostProcessorscopeprototype 改为 singleton
  5. BeanFactoryPostProcessMain 主类,标有 @Configuration@ComponentScan

上述所有类都放在同一个包中

java 复制代码
//  普通类,并没有增加spring注解,会通过 CusBeanDefinitionRegistryPostProcessor 注入到 Spring 容器
public class RegisterComponent {
    public void sayHello() {
        System.out.println("RegisterComponent.sayHello");
    }
}

// 实现了BeanDefinitionRegistryPostProcessor接口,会被spring扫描,并且注入了普通类 RegisterComponent
@Component
public class CusBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(RegisterComponent.class).getBeanDefinition();
        registry.registerBeanDefinition("registerComponent", beanDefinition);
        System.out.println("CusBeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("CusBeanDefinitionRegistryPostProcessor postProcessBeanFactory");
    }
}

//  被spring管理的类,如果没有任何处理会是 prototype 作用域, 这里会通过 CusBeanFactoryPostProcessor 将 scope 改为 singleton
@Component
@Scope("prototype")
public class ScanComponent {
    public void sayHello() {
        System.out.println("BeanFactoryDemo1 sayHello");
    }
}

// 实现了BeanFactoryPostProcessor接口,会将scanComponent类的scope从prototype改为singleton
@Component
public class CusBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition scanComponent = beanFactory.getBeanDefinition("scanComponent");
        scanComponent.setScope("singleton");
        System.out.println("CusBeanFactoryPostProcessor postProcessBeanFactory");
    }
}

// 配置主类
@Configuration
@ComponentScan
public class BeanFactoryPostProcessMain {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanFactoryPostProcessMain.class);
        // BeanFactoryPostProcessor 的测试(修改了 ScanComponent 类的 scope 属性)
        ScanComponent bean = context.getBean(ScanComponent.class);
        System.out.println(bean);
        ScanComponent bean2 = context.getBean(ScanComponent.class);
        System.out.println(bean2);

        RegisterComponent registerComponent = context.getBean(RegisterComponent.class);
        registerComponent.sayHello(); // 会打印 "RegisterComponent.sayHello"
    }
}

执行结果

java 复制代码
CusBeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry
CusBeanDefinitionRegistryPostProcessor postProcessBeanFactory
CusBeanFactoryPostProcessor postProcessBeanFactory
org.example.beanfactorypost.ScanComponent@5e1fa5b1
org.example.beanfactorypost.ScanComponent@5e1fa5b1
RegisterComponent.sayHello

从上面执行结果可以得出如下几个结论:

  1. 先执行实现了 BeanDefinitionRegistryPostProcessor 接口的类,然后才是执行的 BeanFactoryPostProcessor 接口实现类
  2. BeanDefinitionRegistryPostProcessor 实现类确实可以注册普通对象成为 bean, 比如上面注册了普通对象 RegisterComponent
  3. CusBeanFactoryPostProcessor 类已经将 ScanComponentscope 属性改成 singleton, 因为两次 getBean 获取的是同一个对象

BeanFactory 后置处理器的执行时机

参考 [[Spring上下文refresh方法解析]] 中写到的 refresh() 方法的第五步,就是执行后置处理器

执行流程如下

sequenceDiagram participant A as AnnotationConfigApplicationContext participant B as AbstractApplicationContext participant C as PostProcessorRegistrationDelegate A->>A: new AnnotationConfigApplicationContext(classes) A->>B: refresh() B->>C: invokeBeanFactoryPostProcessors() C->>C: invokeBeanFactoryPostProcessors()

所以最终是执行到 public PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() 方法 , 代码这里就不贴了,这里总结一下

  1. 执行实现了 BeanDefinitionRegistryPostProcessorsPriorityOrdered 的类的postProcessBeanDefinitionRegistry() 方法
  2. 执行实现了 BeanDefinitionRegistryPostProcessorsOrdered 的类的postProcessBeanDefinitionRegistry() 方法
  3. 执行实现了 BeanDefinitionRegistryPostProcessors 并且在 前面两步中没有执行过的类
  4. 对于实现了 BeanFactoryPostProcessor 的类,调用流程也是类似,执行实现了 PriorityOrdered 的类 -> 执行实现了 Ordered 的类 -> 执行剩下的类

BeanDefinitionRegistryPostProcessor 典型实现类 ConfigurationClassPostProcessor

Spring 内部也是有很多实现了 BeanDefinitionRegistryPostProcessor 接口的类,比如 ConfigurationClassPostProcessor 类,下面就来看看这个类

  1. 先说明一下 ConfigurationClassPostProcessor 这个类做的事情,在 Spring@ComponentScan 注解可以批量扫描类被 Spring 管理,但是注解只是起到一个标记的作用,需要有地方来解析这个注解才行,而这个解析的源头就是 ConfigurationClassPostProcessor,真正解析是在 ConfigurationClassParser
  2. 前面有说过 BeanDefinitionRegistryPostProcessors 接口的实现类可以批量注册 BeanDefinitionSpring 管理,在 ConfigurationClassParser 中将 @ComponentScan 涉及到的类都解析出来之后,刚好就可以注册到 Spring 容器中

代码执行流程如下

csharp 复制代码
AbstractApplicationContext#refresh
    AbstractApplicationContext#invokeBeanFactoryPostProcessors
        public PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
            private PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
                ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
                    ConfigurationClassPostProcessor#processConfigBeanDefinitions
                        ConfigurationClassParser#parse
                            ComponentScanAnnotationParser#parse

上面是代码的整体执行流程, 最终是通过 ComponentScanAnnotationParser 来进行解析 @ComponentScan 注解的

问题

上面代码执行流程中,在 PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors 中可以得到 ConfigurationClassPostProcessor 类,但是这个类我们并没有手动进行注册。

AbstractApplicationContext 中有 addBeanFactoryPostProcessor() 可以手动注册

java 复制代码
public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
     @Override
	public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
		Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
		this.beanFactoryPostProcessors.add(postProcessor);
	}   
}

所以肯定是 Spring 在哪个步骤中自己注册了,要想找到在哪里注册的也不难,那就是在 ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry() 方法中打断点,然后看调用栈,就可以知道 ConfigurationClassPostProcessor 对象是从哪里获取的,不同的上下文设置的方式可能不同,在给出结论之前再看一下我们的主类测试代码

java 复制代码
@Configuration
@ComponentScan
public class BeanFactoryPostProcessMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanFactoryPostProcessMain.class);
        // BeanFactoryPostProcessor 的测试(修改了 ScanComponent 类的 scope 属性)
        ScanComponent bean = context.getBean(ScanComponent.class);
        System.out.println(bean);
        ScanComponent bean2 = context.getBean(ScanComponent.class);
        System.out.println(bean2);

        RegisterComponent registerComponent = context.getBean(RegisterComponent.class);
        registerComponent.sayHello(); // 会打印 "RegisterComponent.sayHello"
    }
}

从测试代码中看到上下文对象是 AnnotationConfigApplicationContext, 所以下面给出的结论也是基于这个上下文对象, 下面代码执行流程使用序号进行标记

java 复制代码
// new AnnotationConfigApplicationContext(classes) 
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();  // 1
    register(componentClasses);
    refresh();
}

// AnnotationConfigApplicationContext 类
public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this); // 2
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

// AnnotatedBeanDefinitionReader 类
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry)); // 3
}
 
// 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); // 4
}

// AnnotationConfigUtils 类
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null); // 5
}

// AnnotationConfigUtils 类
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    // public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
    // 省略
    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)); // 6
    }
    // 省略
    return beanDefs;
}

// AnnotationConfigUtils 类
private static BeanDefinitionHolder registerPostProcessor(
			BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(beanName, definition); // 7
    return new BeanDefinitionHolder(definition, beanName);
}

// GenericApplicationContext 类
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {
    this.beanFactory.registerBeanDefinition(beanName, beanDefinition); // 8
}


// DefaultListableBeanFactory 类
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
    // 省略
    this.beanDefinitionNames.add(beanName); // 9 
    // 省略
}

至此已经知道 ConfigurationClassPostProcessor 是在何处添加的,而且 ConfigurationClassPostProcessor 类对应的 beanNameorg.springframework.context.annotation.internalConfigurationAnnotationProcessor

相关推荐
TANGLONG2222 小时前
【初阶数据结构与算法】排序算法总结篇(每个小节后面有源码)(直接插入、希尔、选择、堆、冒泡、快速、归并、计数以及非递归快速、归并排序)
java·c语言·数据结构·c++·算法·面试·排序算法
我荔枝呢!2 小时前
集合(List、Set、Map)ArrayList、LinkedList、Vector、HashSet、LinkedHashSet、HashMap
java·开发语言
zhxueverme2 小时前
JAVA学习笔记_Redis进阶
java·笔记·学习
tianyueWindbg4 小时前
IDEA错题集
java·ide·intellij-idea
全栈老实人_5 小时前
旅游管理系统|Java|SSM|VUE| 前后端分离
java·开发语言·tomcat·maven
CoderLi_6 小时前
Java 类加载机制
java·jvm·类加载
问道飞鱼6 小时前
【Springboot知识】Springboot基础-过滤器与拦截器开发
java·spring boot·后端·过滤器·拦截器
拉一次撑死狗6 小时前
SpringCloudAlibaba技术栈-Higress
java·spring boot·分布式·spring cloud·intellij-idea
穿条秋裤到处跑6 小时前
记一次MybatisPlus一级缓存导致的DB对象取值逻辑错误
java·数据库·缓存
yava_free7 小时前
Java的SpringMVC
java