關於Spring BeanPostProcessor的執行順序

Spring BeanPostProcessor執行順序

首先 Spring 通過調用構造方法創建 User 對象;

User 對象創建好之後,先不直接進行初始化操作,通過 BeanPostProcessor 對剛創建好的 User 對象進行加工操作,其中 postProcessBeforeInitialization 方法的第一個參數是 User 對象,第二個參數是在配置文件中指定的 id 值;

加工好之後通過 return 將對象返回給 Spring 容器,然後 Spring 容器繼續按照流程執行 初始化操作,先是 InitializingBean 的初始化操作;

再是 init-method 的初始化;

然後 Spring 容器再次將對象交給 BeanPostProcessor ,執行 postProcessAfterInitialization 方法。

實際上在實戰中,我們很少處理 Spring 的初始化操作,所以沒有必要區分 Before 還是 After。隻需要實現其中的一個即可,顯然選 After 方法更好。

先定義一個實體類 Category:

public class Category {
  private Integer id;
  private String name;
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  @Override
  public String toString() {
      return "Category{" +
              "id=" + id +
              ", name='" + name + '\'' +
              '}';
  }
}

然後註入到 Spring 容器中:

<bean class="edu.lsu.pojo.Category" id="category">
  <property name="id" value="1"/>
  <property name="name" value="迪麗熱巴"/>
</bean>

註意此時的名字是迪麗熱巴。

此時我們定義一個 BeanPostProcessor,實現他的後置處理器方法:

public class MyBeanPostProcessor implements BeanPostProcessor {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      Category category = (Category) bean;
      category.setName("古力娜紮");
      return category;
  }
}

當我們在此時的時候,輸出結果就是古力娜紮;

@Test
public void test7() {
  ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext2.xml");
  Category category = ac.getBean("category", Category.class);
  System.out.println("category = " + category);
}

輸出:

category = Category{id=1, name=’古力娜紮’}

Spring-BeanPostProcessor接口總結

定義

Spring提供瞭一個BeanPostProcessor接口,這個接口的作用在於對於新構造的實例可以做一些自定義的修改。比如如何構造、屬性值的修改、構造器的選擇。

如果想改變Spring容器中bean的一些屬性或者行為,可以通過自定義類實現BeanPostProcessor接口實現。

以下基本Spring-beans 5.0.6版本說明。

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總結

  • postProcessBeforeInitialization方法的作用在於目標對象實例化之後,初始化之前調用,默認返回原始對象,也可以返回一個包裝實例;
  • 如果返回null,接下來的BeanPostProcessors都不會執行
  • postProcessAfterInitialization方法的作用在於目標對象實例化之後,初始化之後調用,默認返回原始對象,也可以返回一個包裝實例;
  • 如果返回null,接下來的BeanPostProcessors都不會執行
  • 初始化(Initialization):表示生成對象,未設置屬性;初始化之前表示bean的初始化回調之前,如InitializingBean接口的afterPropertiesSet方法或者自定義的init-method方法

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor接口繼承自BeanPostProcessor接口,定義瞭3個方法

@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
 return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
 return true;
}
@Nullable
default PropertyValues postProcessPropertyValues(
  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
 return pvs;
}

InstantiationAwareBeanPostProcessor總結

  • postProcessBeforeInstantiation方法在目標對象實例化之前調用,可以返回一個代理對象來代替目標對象本身;如果返回非null對象,則除瞭調用postProcessAfterInitialization方法外,其他bean的構造過程都不再調用;
  • postProcessAfterInstantiation方法在對象實例化之後,屬性設置之前調用;如果返回值是true,目標bean的屬性會被populate,返回false則忽略populate過程;
  • postProcessPropertyValues方法在屬性被設置到目標實例之前調用,可以修改屬性的設置,PropertyValues pvs表示參數值,PropertyDescriptor[] pds表示目標bean 的屬性描述信息,返回值PropertyValues,可以用一個全新的PropertyValues來替代原來的pvs,如果返回null,將忽略屬性設置過程;

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor接口繼承InstantiationAwareBeanPostProcessor接口,定義瞭3個方法,作用是在於目標對象的實例化過程中需要處理的事情。

@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
 return null;
}
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
  throws BeansException {
 return null;
}
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
 return bean;
}

SmartInstantiationAwareBeanPostProcessor總結

  • predictBeanType方法預測Bean的類型,返回預測成功的Class類型,默認或如果不能預測返回null
  • determineCandidateConstructors方法用於選擇合適的構造器,如果類有多個構造器,可以實現這個方法選擇合適的構造器並用於實例化對象;該方法在postProcessBeforeInstantiation方法和postProcessAfterInstantiation方法之間調用,如果postProcessBeforeInstantiation方法返回瞭一個新的實例代替瞭原本該生成的實例,那麼該方法會被忽略;
  • getEarlyBeanReference方法用於解決循環引用問題。比如ReferenceA實例內部有ReferenceB的引用,ReferenceB實例內部有ReferenceA的引用。首先先實例化ReferenceA,實例化完成之後提前把這個bean暴露在ObjectFactory中,然後populate屬性,這個時候發現需要ReferenceB。然後去實例化ReferenceB,在實例化ReferenceB的時候它需要ReferenceA的實例才能繼續,這個時候就會去ObjectFactory中找出瞭ReferenceA實例,ReferenceB順利實例化。ReferenceB實例化之後,ReferenceA的populate屬性過程也成功完成,註入瞭ReferenceB實例。提前把這個bean暴露在ObjectFactory中,這個ObjectFactory獲取的實例就是通過getEarlyBeanReference方法得到的;

DestructionAwareBeanPostProcessor

void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
default boolean requiresDestruction(Object bean) {
 return true;
}

DestructionAwareBeanPostProcessor總結

postProcessBeforeDestruction方法在目標bean被銷毀之前調用,該回調適用於單例bean的使用;

判斷目標bean是否需要回調postProcessBeforeDestruction方法;

總結一下

Spring內部對象bean的生命周期管理有一套完成的體系,並遵循瞭設計模式中的開閉原則(開放擴展,關閉修改),如果想修改bean的相關信息,可以通過Spring提供的擴展點,如BeanPostProcessor接口去處理,這樣做的好處是不需要關心Spring內部處理邏輯,擴展方便。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: