Spring的bean能夠高度擴展,BeanPostProcessor功不可沒,這個接口可以對bean實例做一些自定義修改,比如spring aop就是利用這個接口實現對bean的動態代理。
如何用
使用方法很簡單,實現BeanPostProcessor,然后將自定義的BeanPostProcessor聲明為一個bean就可以了。spring會自動掃描所有實現BeanPostProcessor的bean。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " MyBeanPostProcessor#MyBeanPostProcessor");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " MyBeanPostProcessor#postProcessAfterInitialization");
return bean;
}
}
BeanPostProcessor子接口
子接口內容摘抄自 https://fangjian0423.github.io/2017/06/20/spring-bean-post-processor/
BeanPostProcessor還有一些子接口的定義,分別實現擴展不同的功能,類圖如下所示:
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor接口繼承自BeanPostProcessor接口。多出了3個方法:
//實例化之前調用
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
//實例化之后調用
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
//在這還可以改變屬性
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException;
- InstantiationAwareBeanPostProcessor接口繼承BeanPostProcessor接口,它內部提供了3個方法,再加上BeanPostProcessor接口內部的2個方法,所以實現這個接口需要實現5個方法。InstantiationAwareBeanPostProcessor接口的主要作用在于目標對象的實例化過程中需要處理的事情,包括實例化對象的前后過程以及實例的屬性設置
- postProcessBeforeInstantiation方法是最先執行的方法,它在目標對象實例化之前調用,該方法的返回值類型是Object,我們可以返回任何類型的值。由于這個時候目標對象還未實例化,所以這個返回值可以用來代替原本該生成的目標對象的實例(比如代理對象)。如果該方法的返回值代替原本該生成的目標對象,后續只有postProcessAfterInitialization方法會調用,其它方法不再調用;否則按照正常的流程走
- postProcessAfterInstantiation方法在目標對象實例化之后調用,這個時候對象已經被實例化,但是該實例的屬性還未被設置,都是null。如果該方法返回false,會忽略屬性值的設置;如果返回true,會按照正常流程設置屬性值
- postProcessPropertyValues方法對屬性值進行修改(這個時候屬性值還未被設置,但是我們可以修改原本該設置進去的屬性值)。如果postProcessAfterInstantiation方法返回false,該方法不會被調用。可以在該方法內對屬性值進行修改
- 父接口BeanPostProcessor的2個方法postProcessBeforeInitialization和postProcessAfterInitialization都是在目標對象被實例化之后,并且屬性也被設置之后調用的
- Instantiation表示實例化,Initialization表示初始化。實例化的意思在對象還未生成,初始化的意思在對象已經生成
SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor接口繼承InstantiationAwareBeanPostProcessor接口。多出了3個方法:
// 預測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;
- SmartInstantiationAwareBeanPostProcessor接口繼承InstantiationAwareBeanPostProcessor接口,它內部提供了3個方法,再加上父接口的5個方法,所以實現這個接口需要實現8個方法。SmartInstantiationAwareBeanPostProcessor接口的主要作用也是在于目標對象的實例化過程中需要處理的事情。它是InstantiationAwareBeanPostProcessor接口的一個擴展。主要在Spring框架內部使用
- predictBeanType方法用于預測Bean的類型,返回第一個預測成功的Class類型,如果不能預測返回null。主要在于BeanDefinition無法確定Bean類型的時候調用該方法來確定類型
- 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方法得到的
BeanPostProcessor
BeanPostProcessor接口是最頂層的接口,接口定義:
public interface BeanPostProcessor {
// 初始化之前的操作
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 初始化之后的操作
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
- postProcessBeforeInitialization是指bean在初始化之前需要調用的方法
- postProcessAfterInitialization是指bean在初始化之后需要調用的方法
- postProcessBeforeInitialization和postProcessAfterInitialization方法被調用的時候。這個時候bean已經被實例化,并且所有該注入的屬性都已經被注入,是一個完整的bean
- 這2個方法的返回值可以是原先生成的實例bean,或者使用wrapper包裝這個實例
何時調用
在我們調用getBean()方法時(也可能是注入時),會調用AbstractAutowireCapableBeanFactory的creatBean()方法。在創建bean的過程中,會調用BeanPostProcessor。
在AbstractAutowireCapableBeanFactory類中,有三個方法分別是:applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization、applyBeanPostProcessorsBeforeInstantiation,從名字就可以看出來是調用BeanPostProcessors的各種方法,我們分析一下applyBeanPostProcessorsBeforeInitialization。
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
public List<BeanPostProcessor> getBeanPostProcessors() {
return this.beanPostProcessors;
}
代碼邏輯并不復雜,遍歷所有的beanPostProcessors,然后執行postProcessBeforeInitialization方法,其他方法也是類似的執行方式。beanPostProcessors是類的成員變量,那么這個變量什么時候添加值進去的呢?
何時注冊
這個可以追溯到容器創建的時候,在AbstractApplicationContext的refresh()方法中,調用了registerBeanPostProcessors(beanFactory),這個方法實現邏輯是掃描所有實現了BeanPostProcessor的類型,并把它們注冊到beanPostProcessors中,代碼邏輯不難,可以自己去源碼中查看這個方法的實現邏輯。