连杰李头像
关注
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 阅读笔记 6封面图

《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 阅读笔记 6

《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 阅读笔记 6

2.3 BeanFactoryPostProcessor

BeanFactoryPostProcessor 也是一个后置处理器,与 BeanPostProcessor 不同的是,它并不作用于 IOC 容器中的 Bean,而直接作用于 IOC 容器本身(BeanFactory)​。由于作用的对象不同,因此扩展的时机和逻辑也随之不同,本节对此展开说明。

2.3.1 BeanFactoryPostProcessor 概述

BeanFactoryPostProcessor 的执行时机是在所有非延迟加载的单实例 Bean 初始化之前。可以从 3 个方面来洞察其特征。
其一,官方文档指出,其操作对象是 Bean 的配置元信息(BeanDefinition)​,可对 BeanFactory 中的 BeanDefinition 进行修改和移除;
其二,javadoc 着重强调,BeanFactoryPostProcessor 与 BeanPostProcessor 几乎没有关联,而且原则上不允许程序代码直接访问和调用(由IOC 容器统一调用)​;
其三,BeanFactoryPostProcessor 接口仅定义一个方法 postProcessBeanFactory,传入的参数类型是 ConfigurableListableBeanFactory,这意味着可以修改 BeanFactory。

简而言之,BeanFactoryPostProcessor 的触发时机如图所示,即 BeanDefinition 已经注册到 BeanDefinitionRegistry,但尚未初始化非延迟加载的单实例 Bean。
在这里插入图片描述

2.3.2 BeanFactoryPostProcessor 的使用

下面通过一个示例演示 BeanFactoryPostProcessor 的使用方法,以此来体会 BeanFactoryPostProcessor 对 IOC 容器中 BeanDefinition 的访问和修改。

模拟一个需求场景:构造几个 Color 颜色类的 Bean,并在这些 Bean 创建之前,将 Bean 的名称写入内部属性。该需求与实现BeanNameAware 接口的效果一致,只需要重点关注 BeanFactoryPostProcessor 的使用方式和效果。

1. 声明类

Color 模型不必太复杂,只需要一个 Color 抽象类和两个子类 Red、Green 即可,如代码所示。注意两个子类上要标注 @Component。
在这里插入图片描述

package com.yangjunbo.spring.postprocessor.exampleh.bean;

public abstract class Color {
    
    protected String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}
package com.yangjunbo.spring.postprocessor.exampleh.bean;

import org.springframework.stereotype.Component;

@Component
public class Green extends Color {
    
    @Override
    public String toString() {
        return "Green{" + "name='" + name + '\'' + "}";
    }
}
package com.yangjunbo.spring.postprocessor.exampleh.bean;

import org.springframework.stereotype.Component;

@Component
public class Red extends Color {
    
    @Override
    public String toString() {
        return "Red{" + "name='" + name + '\'' + "}";
    }
}

2. 编写后置处理器

编码之前首先明确一下思路:既然 BeanFactoryPostProcessor 中拿到的参数是 ConfigurableListableBeanFactory,那么就可以操作其 API 取出内部的 BeanDefinition 并进行属性赋值。由于 ConfigurableListableBeanFactory 无法通过类型取出指定的 BeanDefinition 名称,因此只能取出全部 BeanDefinition 名称并逐个判断其 Bean 类型。此外,由于 BeanDefinition 中只能获取到 beanClassName,无法得知父类,因此还需要借助反射机制检查父类,如代码所示。
在这里插入图片描述

package com.yangjunbo.spring.postprocessor.exampleh.config;

import com.yangjunbo.spring.postprocessor.exampleh.bean.Color;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.stream.Stream;

@Component
public class ColorNameSetterFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Stream.of(beanFactory.getBeanDefinitionNames()).forEach(beanName -> {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            String beanClassName = beanDefinition.getBeanClassName();
            if (StringUtils.hasText(beanClassName)) {
                ClassLoader classLoader = this.getClass().getClassLoader();
                Class<?> superclass = ClassUtils.resolveClassName(beanClassName, classLoader).getSuperclass();
                if (superclass.equals(Color.class)) {
                    System.out.println("Color definition name setting ...... " + beanName);
                    beanDefinition.getPropertyValues().add("name", beanName);
                }
            }
        });
    }
}

代码中有几个小细节应注意:不是所有的 BeanDefinition 都有 beanClassName 属性封装,由 1.3.3 节可知,只要 Bean 的注册来源不是通过@Bean 注册,那么就可以获取 beanClassName,而此处使用 @Component 配合组件扫描注册,故可以获取 beanClassName;获取类型名称后使用工具方法 ClassUtils.resolveClassName 进行反射解析判断,可以避免探测类动作引发潜在的 ClassNotFoundException 异常。

3. 测试运行

接下来使用注解驱动 IOC 容器进行组件扫描,从中获取 Red 和 Green 并打印,如代码所示。运行 main 方法,控制台打印出 Red 和 Green 的name 属性,证明 ColorNameSetterFactoryPostProcessor 已生效。
在这里插入图片描述

package com.yangjunbo.spring.postprocessor.exampleh;

import com.yangjunbo.spring.postprocessor.exampleh.bean.Green;
import com.yangjunbo.spring.postprocessor.exampleh.bean.Red;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * ClassName: PostProcessorApplication
 * Package: com.yangjunbo.spring.postprocessor.examplea
 * Description:
 *
 * @Author 杨钧博
 * @Create 2026/9/8 20:27
 * @Version 1.0
 */
public class PostProcessorHApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(
                "com.yangjunbo.spring.postprocessor.exampleh");
        Red red = ctx.getBean(Red.class);
        System.out.println(red);
        System.out.println(ctx.getBean(Green.class));
    }

}

4. 替代方案

读者可能意识到,当前需求除了能使用 BeanNameAware 接口进行回调注入来实现,也能用 BeanPostProcessor 完成,且更为简单。所以这里也提醒读者:对于不同的需求场景,可选用相对恰当的 API,通过综合研判分析找到更合适的解决方案。

5. BeanDefinition 移除回顾

请读者回顾 1.4.2 节的内容,曾借助 BeanFactoryPostProcessor 实现 BeanDefinition 的移除,但代码中 postProcessBeanFactory 方法的第 1 行将 BeanFactory 强转为 BeanDefinitionRegistry,虽然可行但不够规范。接下来要讲解的 BeanDefinitionRegistryPostProcessor 就可以优化此问题。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/Rockandrollman/article/details/165632194

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--