Bootstrap

beanfactorypostprocessor_Spring之BeanFactoryPostProcessor 和BeanPostProcessor

b99fc4b41b94a868b725e7a553263d34.png

概述

BeanFactoryPostProcessor 和 BeanPostProcessor 这两个接口,都是 Spring 初始化 bean 时对外暴露的扩展点,一般叫做 Spring 的 Bean 后置处理器接口,作用是为 Bean 的初始化前后 提供可扩展的空间。两个接口名称看起来很相似,但作用和使用场景却略有不同。

Spring 中 bean 的生命周期图:

65631ea38169e6e82d349cf3024a8b7e.png

由上图可以看到,Spring 中的 BeanFactoryPostProcessor 在实例化之前被调用,而 BeanPostProcessor 则是在实例化过程中使用。

BeanFactoryPostProcessor

@FunctionalInterface

实现该接口,可以在 Spring 创建 bean 之前修改 bean 的定义属性。也就是说,Srping 允许 BeanFactoryPostProcessor 在容器实例化 bean 之前读取配置元数据,并可以根据需要进行修改。例如可以把 bean 的 Scope 从 singleton 改为 prototype ,也可以把 property 的值给修改掉。另外可以同时配置多个 BeanFactoryPostProcessor,并通过 order 属性来控制 BeanFactoryPostProcessor 的执行顺序 ( 在实现 BeanFactoryPostProcessor 时应该考虑实现 Ordered 接口 )。

BeanFactoryPostProcessor 是在 Spring 容器加载了定义 bean 的 XML 文件之后,在 bean 实例化之前执行的。接口方法的入参是 ConfigurrableListableBeanFactory 类型,使用该参数可以获取到相关的 bean 的定义信息。

实际分析

XML 文件配置

首先是 XML 文件,

<?xml version="1.0" encoding="UTF-8"?>

这其中出现了变量:user.name,这是 spring 的分散配置,可以在另外的配置文件中为 user.name 指定值,例如在 bean.properties 文件中定义:

user.name = hresh

当访问名为 user 的 bean 时,其 name 属性就会被字符串 hresh 替换,那 spring 框架是怎么知道存在这样的配置文件呢,这个就是 PropertyPlaceholderConfigurer,需要在配置文件中添加一下代码:

<bean 

PropertyPlaceholderConfigurer

在这个 bean 中指定了配置文件的位置。其实还是有个问题,这个 userHandler 只不过是 spring 框架管理的一个 bean,并没有被别的 bean 或者对象引用,spring 的beanFactory 是怎么知道这个需要从这个 bean 中获取配置信息呢?我们看下 PropertyPlaceholderConfigurer 这个类的层次结构,如下图:

5c2432b3668fcfbdd975cc09f5805715.png

从上图可以看到 PropertyPlaceholderConfigurer 间接的继承了 BeanFactoryPostProcessor 接口,这是一个特别的接口,当 Spring 加载任何实现了该接口的 bean 的配置时,都会在 bean 工厂载入所有 bean 的配置之后执行 postProcessBeanFactory 方法。在 PropertyResourceConfigurer 类中实现了 postProcessBeanFactory 方法,该方法中先后调用了 mergeProperties、convertProperties、processProperties 这三个方法 ,先得到配置,将得到的配置转换为合适的类型,最后将配置内容告知 BeanFactory。

正是通过实现 BeanFactoryPostProcessor 接口,BeanFactory 会在实例化任何 bean之前获得配置信息,从而能够正确的解析 bean 描述文件中的变量引用。

public 

关于 PropertyPlaceholderConfigurer 类的详细解析,有兴趣的朋友可以阅读:【Spring源码分析】.properties文件读取及占位符${...}替换源码解析

自定义BeanFactoryPostProcessor

编写实现了 BeanFactoryPostProcessor 接口的 MyBeanFactoryPostProcessor 的容器后处理器,如下代码:

public 

然后在配置文件中注册这个 bean,如下:

<bean 

最后编写测试代码:

@Test

此时的执行结果为:

对容器进行处理后

再编写一个实现了 BeanFactoryPostProcessor 接口的 UserFactoryPostProcessor 类,用来修改 bean 的属性和 Scope。

public 

然后在配置文件中注册这个 bean,如下:

<bean 

调用测试代码,结果为:

对容器进行处理后

如果要控制 BeanFactoryPostProcessor 的实现类的执行顺序 ,可以这样来修改代码:

public 

再次调用测试代码,输出结果为:

调用UserFactoryPostProcessor的postProcessBeanFactory方法

从结果可以看出,通过实现 Ordered 接口,并重写 getOrder 方法,UserFactoryPostProcessor 比 MyBeanFactoryPostProcessor 更早执行。

BeanPostProcessor

public 

BeanPostProcessor 可以在 spring 容器实例化 bean 之后,在执行 bean 的初始化方法前后,添加一些自己的处理逻辑。 这里说的初始化方法,指的是以下两种:

  1. bean 实现 了 InitializingBean 接口,对应的方法为 afterPropertiesSet 。
  2. 在 XML 文件中定义 bean 的时候,<bean>标签有个属性叫做 init-method,来指定初始化方法。

注意:BeanPostProcessor 是在 spring 容器加载了 bean 的定义文件并且实例化 bean 之后执行的。BeanPostProcessor 的执行顺序是在 BeanFactoryPostProcessor 之后。

实战分析

首先我们定义一个 bean 类:

public 

接着再定义一个 BeanFactoryPostProcessor 实现类:

public 

再定义一个 BeanPostProcessor 实现类:

public 

修改 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>

最后编写测试代码:

@Test

执行结果为:

调用PersonFactoryPostProcessor的postProcessBeanFactory方法

分析

从上述结果可以看出, BeanFactoryPostProcessor 在 bean 实例化之前被调用,注意在 PersonFactoryPostProcessor 的 postProcessBeanFactory 方法中只是修改了 bean 的定义信息,即将 age 值由18改为23,此时 bean 还未实例化。 之后实例化bean,在此过程中,先调用 BeanPostProcessor 实现类中的 postProcessBeforeInitialization 方法,然后调用实现了 InitializingBean接口的 bean 类中的 afterPropertiesSet 方法,如果设置的有 init-method 方法,则也会被调用,最后再调用 BeanPostProcessor 实现类中的 postProcessAfterInitialization 方法。

如果有多个 bean 类的情况呢,BeanFactoryPostProcessor 和 BeanPostProcessor 又是如何工作的?

复用上文中的 User 类和 UserFactoryPostProcessor,然后修改 PersonFactoryPostProcessor 。

public 

在 XML 文件中增加对 User 类的定义

<bean 

修改测试代码

@Test

执行结果为:

调用UserFactoryPostProcessor的postProcessBeanFactory方法

从结果中可以得知,BeanFactoryPostProcessor 的实现类都需要在 XML 文件中进行配置。当你有多个 bean 类时,就需要实现多个 BeanFactoryPostProcessor ,然后在 XML 文件中进行配置,当然你也可以只写在一个实现类当中,比如说在 PersonFactoryPostProcessor 类中这样定义:

public 

这样就不用在 XML 文件中配置,但是这样不利于代码的维护。

反观 BeanPostProcessor,只需要定义一个实现类,如果需要对实例化的 bean 对象进行修改,可以让 bean 类实现 InitializingBean 接口,然后编写 afterPropertiesSet 方法,这样遇到多个 bean 类的时候也比较方便处理。

总结

BeanFactoryPostProcessor 和 BeanPostProcessor 都是服务于 bean 的生命周期中的,只是使用场景和作用略有不同。BeanFactoryPostProcessor 作用于 bean 实例化之前,读取配置元数据,并且可以修改;而 BeanPostProcessor 作用于 bean 的实例化过程中,然后可以改变 bean 实例(例如从配置元数据创建的对象)。

;