Bean 的生命週期回撥

sc_ik發表於2019-01-19

我們可能會在 Bean 物件建立完成後, 執行某些操作或在銷燬前做某些操作.

我們可以實現 InitializingBeanDisposableBean 介面

public class Test implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {

    }
    
    @Override
    public void destroy() throws Exception {
        
    }

}

當然我們可以使用註解來實現

@PostConstruct
public void afterPropertiesSet() throws Exception {
    System.out.println("-- init --");
}

@PreDestroy
public void destroy() throws Exception {
    System.out.println("-- destroy --");
}

還有另一種方法可以使用 @Bean 註解

public void afterPropertiesSet() throws Exception {
    System.out.println("-- init --");
}

public void destroy() throws Exception {
    System.out.println("-- destroy --");
}

@Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy")
public Test getTest() {
    return new Test();
}

要注意的是, 初始化和銷燬的方法只是針對當前的 bean 不會對其它 bean 產生影響.

如果想所有的 bean 在建立前或建立後新增一些處理邏輯, 可以使用 BeanPostProcessor 介面. 也可以配合 Ordered 介面來設定執行順序.

如果有兩個容器, 他們之間不會相互影響.

BeanPostProcessor 介面由兩個回撥方法組成.

public interface BeanPostProcessor {
    // 初始化之前的操作
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    // 初始化之後的操作
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

四個子介面:

1.DestructionAwareBeanPostProcessor

// 該方法是bean在Spring在容器中被銷燬之前呼叫
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

2.InstantiationAwareBeanPostProcessor

// postProcessBeforeInstantiation方法的作用在目標物件被例項化之前呼叫的方法,可以返回目標例項的一個代理用來代替目標例項
// beanClass參數列示目標物件的型別,beanName是目標例項在Spring容器中的name
// 返回值型別是Object,如果返回的是非null物件,接下來除了postProcessAfterInitialization方法會被執行以外,其它bean構造的那些方法都不再執行。否則那些過程以及postProcessAfterInitialization方法都會執行
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

// postProcessAfterInstantiation方法的作用在目標物件被例項化之後並且在屬性值被populate之前呼叫
// bean引數是目標例項(這個時候目標物件已經被例項化但是該例項的屬性還沒有被設定),beanName是目標例項在Spring容器中的name
// 返回值是boolean型別,如果返回true,目標例項內部的返回值會被populate,否則populate這個過程會被忽視
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

// postProcessPropertyValues方法的作用在屬性中被設定到目標例項之前呼叫,可以修改屬性的設定
// pvs參數列示引數屬性值(從BeanDefinition中獲取),pds代表引數的描述資訊(比如引數名,型別等描述資訊),bean引數是目標例項,beanName是目標例項在Spring容器中的name
// 返回值是PropertyValues,可以使用一個全新的PropertyValues代替原先的PropertyValues用來覆蓋屬性設定或者直接在引數pvs上修改。如果返回值是null,那麼會忽略屬性設定這個過程(所有屬性不論使用什麼註解,最後都是null)
PropertyValues postProcessPropertyValues(
    PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
    throws BeansException;

注: 此介面為專用介面, 主要用於框架內部使用. 建議儘可能地實現普通 BeanPostProcessor 介面, 或 InstantiationAwareBeanPostProcessorAdapter 派生, 以避免擴充套件到該介面.

  1. InstantiationAwareBeanPostProcessor介面繼承BeanPostProcessor介面,它內部提供了3個方法,再加上BeanPostProcessor介面內部的2個方法,所以實現這個介面需要實現5個方法。InstantiationAwareBeanPostProcessor介面的主要作用在於目標物件的例項化過程中需要處理的事情,包括例項化物件的前後過程以及例項的屬性設定
  2. postProcessBeforeInstantiation方法是最先執行的方法,它在目標物件例項化之前呼叫,該方法的返回值型別是Object,我們可以返回任何型別的值。由於這個時候目標物件還未例項化,所以這個返回值可以用來代替原本該生成的目標物件的例項(比如代理物件)。如果該方法的返回值代替原本該生成的目標物件,後續只有postProcessAfterInitialization方法會呼叫,其它方法不再呼叫;否則按照正常的流程走
  3. postProcessAfterInstantiation方法在目標物件例項化之後呼叫,這個時候物件已經被例項化,但是該例項的屬性還未被設定,都是null。如果該方法返回false,會忽略屬性值的設定;如果返回true,會按照正常流程設定屬性值
  4. postProcessPropertyValues方法對屬性值進行修改(這個時候屬性值還未被設定,但是我們可以修改原本該設定進去的屬性值)。如果postProcessAfterInstantiation方法返回false,該方法不會被呼叫。可以在該方法內對屬性值進行修改
  5. 父介面BeanPostProcessor的2個方法postProcessBeforeInitialization和postProcessAfterInitialization都是在目標物件被例項化之後,並且屬性也被設定之後呼叫的
  6. Instantiation表示例項化,Initialization表示初始化。例項化的意思在物件還未生成,初始化的意思在物件已經生成

3.SmartInstantiationAwareBeanPostProcessor

// 預測Bean的型別,返回第一個預測成功的Class型別,如果不能預測返回null
Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;

// 選擇合適的構造器,比如目標物件有多個構造器,在這裡可以進行一些定製化,選擇合適的構造器
// beanClass參數列示目標例項的型別,beanName是目標例項在Spring容器中的name
// 返回值是個構造器陣列,如果返回null,會執行下一個PostProcessor的determineCandidateConstructors方法;否則選取該PostProcessor選擇的構造器
Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;

// 獲得提前暴露的bean引用。主要用於解決迴圈引用的問題
// 只有單例物件才會呼叫此方法
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
  1. SmartInstantiationAwareBeanPostProcessor介面繼承InstantiationAwareBeanPostProcessor介面,它內部提供了3個方法,再加上父介面的5個方法,所以實現這個介面需要實現8個方法。SmartInstantiationAwareBeanPostProcessor介面的主要作用也是在於目標物件的例項化過程中需要處理的事情。它是InstantiationAwareBeanPostProcessor介面的一個擴充套件。主要在Spring框架內部使用
  2. predictBeanType方法用於預測Bean的型別,返回第一個預測成功的Class型別,如果不能預測返回null。主要在於BeanDefinition無法確定Bean型別的時候呼叫該方法來確定型別
  3. determineCandidateConstructors方法用於選擇合適的構造器,比如類有多個構造器,可以實現這個方法選擇合適的構造器並用於例項化物件。該方法在postProcessBeforeInstantiation方法和postProcessAfterInstantiation方法之間呼叫,如果postProcessBeforeInstantiation方法返回了一個新的例項代替了原本該生成的例項,那麼該方法會被忽略
  4. getEarlyBeanReference主要用於解決迴圈引用問題。比如ReferenceA例項內部有ReferenceB的引用,ReferenceB例項內部有ReferenceA的引用。首先先例項化ReferenceA,例項化完成之後提前把這個bean暴露在ObjectFactory中,然後populate屬性,這個時候發現需要ReferenceB。然後去例項化ReferenceB,在例項化ReferenceB的時候它需要ReferenceA的例項才能繼續,這個時候就會去ObjectFactory中找出了ReferenceA例項,ReferenceB順利例項化。ReferenceB例項化之後,ReferenceA的populate屬性過程也成功完成,注入了ReferenceB例項。提前把這個bean暴露在ObjectFactory中,這個ObjectFactory獲取的例項就是通過getEarlyBeanReference方法得到的

4.MergedBeanDefinitionPostProcessor

// 該方法是bean在合併Bean定義之後呼叫
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

相關文章