通过实现 BeanFactoryPostProcessor 接口干预 Spring Bean 的注册

总结自:一文吃透 Spring Boot 扩展之 BeanFactoryPostProcessor 概述 BeanFactoryPostSpring 的 BeanDefinitionRegistryPostProcessor 接口详解

什么是 BeanFactoryPostProcessor

BeanFactoryPostProcessor 是一个 Spring 提供的接口,是 Spring 中一个相当重要的扩展点,可用来对 Bean 注册进行修改扩展。

BeanFactoryPostProcessor,翻译过来即 Bean 工厂处理器。BeanFactoryPostProcessor 源码:

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

postProcessBeanFactory 方法执行时机:所有的 Bean 定义(BeanDefinition)已经被加载,但标准 Bean 的实例还没被创建时(不包括 BeanFactoryPostProcessor 类型)。该方法通常用于修改 Bean 的定义,Bean 的属性值等,甚至可以在此快速初始化 Bean。

另外一个相关的扩展接口的 BeanDefinitionRegistryPostProcessor,继承自 BeanFactoryPostProcessor,在 BeanFactoryPostProcessor 的基础上新增了一个方法:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

扩展的方法在所有的 Bean 定义即将被加载,但 Bean 的实例还没被创建时执行,也就是说,BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法执行时机先于 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法。

区别于一个很类似的扩展接口 BeanPostProcessor,它的执行时机是在 Bean 初始化前后(此时 Bean 实例已经创建)。

小结一下,上面关联的扩展接口执行顺序如下:

  1. BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry

  2. BeanFactoryPostProcessor.postProcessBeanFactory

  3. BeanPostProcessor.postProcessBeforeInitialization

  4. BeanPostProcessor.postProcessAfterInitialization

如何使用 BeanFactoryPostProcessor

1、新建一个测试 Bean

@Data
@Component
public class Student {

    @Value("${user.username:alvin}")
    private String username;

    @Value("${user.age:12}")
    private int age;
}

注意 username 的默认值为 alvin。

2、新建处理器实现 BeanFactoryPostProcessor

@Component
@Slf4j
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("*** TestBeanFactoryPostProcessor#postProcessBeanFactory ***");
        // 修改 Bean definition 属性信息
        BeanDefinition userBeanDef = beanFactory.getBeanDefinition("student");
        userBeanDef.getPropertyValues().add("username", "ming");

        // 快速初始化 Bean
        User user = (User) beanFactory.getBean("student");
        log.info("student name: [{}]", user.getUsername());
    }
}

3、验证结论

*** TestBeanFactoryPostProcessor#postProcessBeanFactory ***
student name: [ming]

从日志可以看到 Bean 的属性被修改了。

源码解析

接口说明

BeanDefinitionRegistryPostProcessor 接口的注释对它的作用和细节作了说明:

  • 该接口允许用户自定义修改工厂 Bean 中的 BeanDefinition,调整 BeanDefinition 的属性值,甚至初始化 Bean。比如内置的 PropertyResourceConfigurer,实现该接口以修改 BeanDefinition 的属性为配置文件的属性。
  • 该接口主要是用于修改 BeanDefinition,虽然也可以直接进行实例化 Bean,但是不建议这么做,可能会造成其他未知的错误。

postProcessBeanDefinitionRegistry 执行时机

AbstractApplicationContext#refresh 方法调用了 AbstractApplicationContext#invokeBeanFactoryPostProcessors

img

其进一步调用了 PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors。该方法的前面部分逻辑主要是处理 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,后面部分主要处理 BeanFactoryPostProcessor 接口的 postProcessBeanFactory 方法。

PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors 优先执行实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessor,其次就是实现了接口 Ordered 的,最后才是两者都没有实现的。

img

调用 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法。

postProcessBeanFactory 执行时机

跟 BeanDefinitionRegistryPostProcessor 的调用路径一样,在调用了所有的 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法后,紧接着就会处理所有的 BeanFactoryPostProcessor,按优先级调用其 postProcessBeanFactory 接口。

优先执行实现了 PriorityOrdered 接口的 BeanFactoryPostProcessor,其次就是实现了接口 Ordered 的,最后才是两者都没有实现的。

img
img

小结

整个执行流程的源码还是比较清晰并且简单的。重点提下下面两个点:

1、我们自定义的 BeanFactoryProcessor 需要加上 @Component 等注解,为什么?

看源码得知,String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);获取所有实现了 BeanFactoryPostProcessor 接口的 Bean 名称,前提所有的 Bean 都要被注册到 BeanDefinitionRegistry,通过添加 @Component,@Service 等注解,可以将对应的 Bean 定义信息注册到 BeanFactory 中,方便后面实例化 Bean。那么它是在什么地方注册的呢?可以看下 ConfigurationClassPostProcessor 类,它实现了 BeanDefinitionRegistryPostProcessor,会扫描所有 @Component,@Service 等注解,将对应的 Bean Definition 注册到 BeanFactory 中。

2、执行顺序问题

我们可以通过实现 PriorityOrdered,Ordered 接口,控制 BeanFactoryProcessor 的执行顺序,,优先执行实现了 PriorityOrdered 接口,其次是 Ordered,最后是没有实现任何排序接口的 processor。

posted @ 2024-12-02 21:15  Higurashi-kagome  阅读(76)  评论(0)    收藏  举报