Spring之BeanFactoryPostProcessor和BeanPostProcessor

概述BeanFactoryPostProcessor實際分析XML 文件配置PropertyPlaceholderConfigurer自定義BeanFactoryPostProcessorBeanPostProcessor實戰分析總結html

概述

BeanFactoryPostProcessor 和 BeanPostProcessor 這兩個接口,都是 Spring 初始化 bean 時對外暴露的擴展點,通常叫作 Spring 的 Bean 後置處理器接口,做用是爲 Bean 的初始化先後 提供可擴展的空間。兩個接口名稱看起來很類似,但做用和使用場景卻略有不一樣。java

Spring 中 bean 的生命週期圖:web

由上圖能夠看到,Spring 中的 BeanFactoryPostProcessor 在實例化以前被調用,而 BeanPostProcessor 則是在實例化過程當中使用。spring

BeanFactoryPostProcessor

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
複製代碼

實現該接口,能夠在 Spring 建立 bean 以前修改 bean 的定義屬性。也就是說,Srping 容許 BeanFactoryPostProcessor 在容器實例化 bean 以前讀取配置元數據,並能夠根據須要進行修改。例如能夠把 bean 的 Scope 從 singleton 改成 prototype ,也能夠把 property 的值給修改掉。另外能夠同時配置多個 BeanFactoryPostProcessor,並經過 order 屬性來控制 BeanFactoryPostProcessor 的執行順序 ( 在實現 BeanFactoryPostProcessor 時應該考慮實現 Ordered 接口 )。app

BeanFactoryPostProcessor 是在 Spring 容器加載了定義 bean 的 XML 文件以後,在 bean 實例化以前執行的。接口方法的入參是 ConfigurrableListableBeanFactory 類型,使用該參數能夠獲取到相關的 bean 的定義信息。框架

實際分析

XML 文件配置

首先是 XML 文件,ide

<?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 這個類的層次結構,以下圖:post

從上圖能夠看到 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");
        }

        beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
        MutablePropertyValues pv2 = beanDefinition.getPropertyValues();
        if (pv2.contains("name")){
            pv2.addPropertyValue("name","acorn22");
        }

    }
}
複製代碼

在 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 實例(例如從配置元數據建立的對象)。

相關文章
相關標籤/搜索