Spring之BeanFactoryPostProcessor和BeanPostProcessor

简介: Spring之BeanFactoryPostProcessor和BeanPostProcessor

概述


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


Spring 中 bean 的生命周期图:


image.png


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


BeanFactoryPostProcessor


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


实现该接口,可以在 Spring 创建 bean 之前修改 bean 的定义属性。也就是说,Spring 允许 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"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.msdn.bean.User">
        <!--<property name="name" value="hresh" />-->
        <property name="name" value="${user.name}" />
    </bean>
</beans>
复制代码


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


user.name = hresh
复制代码


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


<bean id="userHandler" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:bean.properties</value>
        </list>
    </property>
</bean>
复制代码


PropertyPlaceholderConfigurer


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


image.png


从上图可以看到 PropertyPlaceholderConfigurer 间接的继承了

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


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


public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
        Properties mergedProps = this.mergeProperties();
        this.convertProperties(mergedProps);
        this.processProperties(beanFactory, mergedProps);
    } catch (IOException var3) {
        throw new BeanInitializationException("Could not load properties", var3);
    }
}
复制代码


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


自定义BeanFactoryPostProcessor


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

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("对容器进行处理后。。。。。");
    }
}
复制代码


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


<bean id="myPost" class="com.msdn.processor.MyBeanFactoryPostProcessor"></bean>
复制代码


最后编写测试代码:


@Test
public void MyBean(){
    //解析application_context.xml文件 , 生成管理相应的Bean对象
    ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
    User user = (User) context.getBean("user");
    System.out.println(user);
    System.out.println(user.hashCode());
    User user1 = (User) context.getBean("user");
    System.out.println(user1);
    System.out.println(user1.hashCode());
}
复制代码


此时的执行结果为:


对容器进行处理后。。。。。
User{name='hresh'}
681384962
User{name='hresh'}
681384962
复制代码


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


public class UserFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用UserFactoryPostProcessor的postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        MutablePropertyValues pv = beanDefinition.getPropertyValues();
        if (pv.contains("name")){
            pv.addPropertyValue("name","acorn");
        }
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
}
复制代码


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


<bean id="myUser" class="com.msdn.processor.UserFactoryPostProcessor"></bean>
复制代码


调用测试代码,结果为:


对容器进行处理后。。。。。
调用UserFactoryPostProcessor的postProcessBeanFactory方法
User{name='acorn'}
586084331
User{name='acorn'}
399534175
复制代码


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


public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor,Ordered {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("对容器进行处理后。。。。。");
    }
    public int getOrder() {
        return 1;
    }
}
复制代码
public class UserFactoryPostProcessor implements BeanFactoryPostProcessor,Ordered {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用UserFactoryPostProcessor的postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        MutablePropertyValues pv = beanDefinition.getPropertyValues();
        if (pv.contains("name")){
            pv.addPropertyValue("name","acorn");
        }
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
    public int getOrder() {
        return 0;
    }
}
复制代码


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


调用UserFactoryPostProcessor的postProcessBeanFactory方法
对容器进行处理后。。。。。
User{name='acorn'}
586084331
User{name='acorn'}
399534175
复制代码


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


BeanPostProcessor


public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
复制代码


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


  1. bean 实现 了 InitializingBean 接口,对应的方法为 afterPropertiesSet 。
  2. 在 XML 文件中定义 bean 的时候,标签有个属性叫做  init-method,来指定初始化方法。
    注意:BeanPostProcessor 是在 spring 容器加载了 bean 的定义文件并且实例化 bean 之后执行的。BeanPostProcessor 的执行顺序是在 BeanFactoryPostProcessor 之后。


实战分析


首先我们定义一个 bean 类:


public class Person implements InitializingBean {
    private String name;
    private int age;
    public Person() {
        System.out.println("Person的无参构造函数");
    }
    public Person(String name, int age) {
        System.out.println("Person的有参构造函数,正在创建Person对象");
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用afterPropertiesSet方法,此时用来修改name属性值");
        this.name = "acorn";
    }
    public void init(){
        System.out.println("调用init方法");
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
复制代码


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


public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
        MutablePropertyValues pv = beanDefinition.getPropertyValues();
        if (pv.contains("age")){
            pv.addPropertyValue("age","23");
        }
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
}
复制代码


再定义一个  BeanPostProcessor  实现类:


public class PersonPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之前的数据: " + bean.toString());
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之后的数据:" + bean.toString());
        Person person = (Person) bean;
        person.setName("clearLove");
        return person;
    }
}
复制代码


修改 XML 文件:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="personPostProcessor" class="com.msdn.processor.PersonPostProcessor" />
    <bean id="personFactoryPostProcessor" class="com.msdn.processor.PersonFactoryPostProcessor" />
    <bean id="person" class="com.msdn.bean.Person" init-method="init">
        <property name="name" value="hresh" />
        <property name="age" value="18" />
    </bean>
</beans>
复制代码


最后编写测试代码:


@Test
public void MyBean(){
    //解析application_context.xml文件 , 生成管理相应的Bean对象
    ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
    Person person = (Person) context.getBean("person");
    System.out.println(person);
    System.out.println(person.hashCode());
    //        Person person2 = (Person) context.getBean("person");
    //        System.out.println(person2);
    //        System.out.println(person2.hashCode());
}
复制代码


执行结果为:


调用PersonFactoryPostProcessor的postProcessBeanFactory方法
Person的无参构造函数
BeanPostProcessor,对象person调用初始化方法之前的数据: Person{name='hresh', age=23}
调用afterPropertiesSet方法,此时用来修改name属性值
调用init方法
BeanPostProcessor,对象person调用初始化方法之后的数据:Person{name='acorn', age=23}
Person{name='clearLove', age=23}
2015781843
复制代码


分析


从上述结果可以看出, 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 class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
        MutablePropertyValues pv = beanDefinition.getPropertyValues();
        if (pv.contains("age")){
            pv.addPropertyValue("age","23");
        }
    }
}
复制代码


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


<bean id="user" class="com.msdn.bean.User">
    <property name="name" value="hresh" />
</bean>
<bean id="myUser" class="com.msdn.processor.UserFactoryPostProcessor"></bean>
复制代码


修改测试代码


@Test
public void MyBean(){
    //解析application_context.xml文件 , 生成管理相应的Bean对象
    ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
    User user1 = (User) context.getBean("user");
    System.out.println(user1);
    System.out.println(user1.hashCode());
    Person person = (Person) context.getBean("person");
    System.out.println(person);
    System.out.println(person.hashCode());
    //        Person person2 = (Person) context.getBean("person");
    //        System.out.println(person2);
    //        System.out.println(person2.hashCode());
}
复制代码


执行结果为:


调用UserFactoryPostProcessor的postProcessBeanFactory方法
调用PersonFactoryPostProcessor的postProcessBeanFactory方法
Person的无参构造函数
BeanPostProcessor,对象person调用初始化方法之前的数据: Person{name='hresh', age=23}
调用afterPropertiesSet方法,此时用来修改name属性值
调用init方法
BeanPostProcessor,对象person调用初始化方法之后的数据:Person{name='acorn', age=23}
BeanPostProcessor,对象user调用初始化方法之前的数据: User{name='acorn'}
BeanPostProcessor,对象user调用初始化方法之后的数据:User{name='acorn'}
User{name='acorn'}
1682463303
Person{name='acorn', age=23}
633075331
复制代码


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


public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
        MutablePropertyValues pv = beanDefinition.getPropertyValues();
        if (pv.contains("age")){
            pv.addPropertyValue("age","23");
        }
        beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        MutablePropertyValues pv2 = beanDefinition.getPropertyValues();
        if (pv2.contains("name")){
            pv2.addPropertyValue("name","acorn22");
        }
    }
}
复制代码


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


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


总结


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



目录
相关文章
|
4月前
|
Java Spring
Spring中那些BeanPostProcessor在Bean实例化过程中的作用
Spring中那些BeanPostProcessor在Bean实例化过程中的作用
48 1
|
8月前
|
Java Spring 容器
深入理解Spring源码之IOC 扩展原理BeanFactoryPostProcessor和事件监听ApplicationListener
深入理解Spring源码之IOC 扩展原理BeanFactoryPostProcessor和事件监听ApplicationListener
|
7月前
|
XML 开发框架 Java
聊聊Spring扩展点BeanPostProcessor和BeanFactoryPostProcessor
今天聊一聊spring中很重要的两个扩展点BeanPostProcessor和BeanFactoryPostProcessor,spring之所以如次强大,是因为它提供了丰富的功能给我们使用,但是我觉得最强大的是它扩展点,因为有了各种扩展点,我们才能去开发一些自己的需求,一个框架的强大之处也在于它能否灵活的配置,能够支持很好的扩展。
37 0
|
8月前
|
Java Spring 容器
深入理解Spring源码之bean的生命周期控制器BeanPostProcessor
深入理解Spring源码之bean的生命周期控制器BeanPostProcessor
|
8月前
|
XML Java 数据格式
Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析
在Spring框架中,BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor是两个重要的后置处理器,它们在容器的启动过程中起到了至关重要的作用。本文深入探讨了这两者的定义、功能、执行时机以及如何在实际项目中使用它们。同时,通过对比分析,为读者揭示了它们与其他后置处理器之间的差异。无论你是初学者还是有一定经验的开发者,都能从中获得深入的理解和启示。
276 2
Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析
|
10月前
|
缓存 Java 开发者
Spring高手之路6——Bean生命周期的扩展点:BeanPostProcessor
在本篇文章中,我们将深入探讨Spring框架中的重要组件——BeanPostProcessor。首先,我们将了解其设计理念和目标,然后通过实际的例子学习如何基础使用它,如何通过BeanPostProcessor改变Bean的初始化结果以及如何利用它修改Bean的属性。最后,我们将深入理解后置处理器在Bean生命周期中的作用和执行时机,帮助读者更好地理解和使用这个强大的工具。
65 1
Spring高手之路6——Bean生命周期的扩展点:BeanPostProcessor
|
10月前
|
XML Java 数据库连接
深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor
深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor
64 1
|
Java 数据库连接 Spring
Spring源码分析之BeanFactoryPostProcessor调用过程(二)
Spring源码分析之BeanFactoryPostProcessor调用过程
89 0
|
Java 数据库连接 Spring
Spring源码分析之BeanFactoryPostProcessor调用过程(一)
Spring源码分析之BeanFactoryPostProcessor调用过程
88 0
|
Java Spring 容器
Spring - BeanFactoryPostProcessor 扩展接口
BeanFactoryPostProcessor 扩展接口
98 0
Spring - BeanFactoryPostProcessor 扩展接口