2、如何在Bean初始化回調前後進行自定義操作
在Spring環境中,如果需要在bean自動裝配(屬性都注入ok)完成後進行自定義操作,通常只需要實現接口InitializingBean,在afterPropertiesSet方法中執行操作即可。在這個接口回調時,bean中所有的屬性已經注入完成了。比如在bean初始化完成後添加一段log:
@Component
public class MyBean implements InitializingBean {
private Logger log = LoggerFactory.getLogger(MyBean.class);
@Override
public void afterPropertiesSet() throws Exception {
log.info("=======>afterPropertiesSet");
}
}
項目啓動後可以看到log輸出
INFO com.cml.chat.lesson.lesson2.MyBean - =======>afterPropertiesSet
這樣的實現方式適合單獨的bean,如果有多個bean都具有一樣的業務邏輯,那麼抽象出一個共同類出來即可。所有的類都繼承這個抽象類,但是這樣的方式代碼入侵大,不太適合用於框架類的項目和共通業務邏輯處理。那麼如何更優雅的在bean初始化前後自定義進行處理呢?這時候BeanPostProcessor就派上用場了。
BeanPostProcessor
BeanPostProcessor接口提供了
- postProcessBeforeInitialization
在bean初始化回調前調用
- postProcessAfterInitialization
在bean初始化回調完成後進行調用,而且會在afterPropertiesSet方法回調之後。
兩個方法,可以在bean初始化回調前後進行處理。而且使用也非常簡單,只需要實現BeanPostProcessor接口就可以在bean初始化回調前後進行處理其他業務邏輯。這裏做個簡單的使用示例:
先定義好IMyBean接口,提供setCustomValue,getCustomValue兩個方法,當所有IMyBean對象getCustomValue獲取數據爲空時,自動設置customValue爲默認值"defaultValue";
public interface IMyBean {
void setCustomValue(String v);
String getCustomValue();
}
定義好MyBean實現IMyBean接口
@Component
public class MyBean implements IMyBean {
private String customValue;
public String getCustomValue() {
return customValue;
}
public void setCustomValue(String customValue) {
this.customValue = customValue;
}
}
添加MyBeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
private Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof IMyBean) {
log.info("=======>postProcessAfterInitialization");
IMyBean mybean = (IMyBean) bean;
if (mybean.getCustomValue() == null) {
mybean.setCustomValue("defaultValue");
}
}
return bean;
}
}
運行:
@SpringBootApplication()
public class Application {
private static Logger log=LoggerFactory.getLogger(Application.class);
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class);
// 非web環境
springApplication.setWebEnvironment(false);
ConfigurableApplicationContext application = springApplication.run(args);
MyBean mybean = application.getBean(MyBean.class);
log.info("getCustomValue:"+mybean.getCustomValue());
}
}
log:
com.cml.chat.lesson.lesson2.Application - getCustomValue:defaultValue
當設置了customValue時,
String customValue = "myCustomValue";
輸出:
com.cml.chat.lesson.lesson2.Application - getCustomValue:myCustomValue
這樣簡單的BeanPostProcessor就實現了,那麼爲什麼只要實現BeanPostProcessor接口就可以了?Spring是如何識別BeanPostProcessor的呢?那麼這時候該來剖析下BeanPostProcessor實現原理了。
BeanPostProcessor原理解析
在Spring中,所有的Bean都是通過BeanFactory進行管理的,SpringBoot中使用的是DefaultListableBeanFactory。可以從以下代碼獲取SpringBoot使用的BeanFactory
@SpringBootApplication()
public class Application {
private static Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class);
// 非web環境
springApplication.setWebEnvironment(false);
ConfigurableApplicationContext application = springApplication.run(args);
log.info("beanFactory===>" + application.getBeanFactory().getClass());
application.close();
}
}
輸出log:
com.cml.chat.lesson.lesson2.Application - beanFactory===>class org.springframework.beans.factory.support.DefaultListableBeanFactory
當獲取bean時,都是通過調用BeanFactory.getBean方法獲得的。在SpingBoot中BeanFactory默認使用的是DefaultListableBeanFactory。知道了BeanFactory就相當於找到了所有bean相關的入口。那麼剩下的就該來剖析下BeanPostProcessor實現原理了。
首先先說下一個萬能的原理查看方法:
只要在對應的調用地方添加斷點,當斷點進入後就可以看到整個方法調用鏈,這樣就可以知道整個流程是如何運作了。就從上面的例子來說在MyBeanPostProcessor.postProcessBeforeInitialization方法中添加斷點,debug運行後可得到如下
是不是非常直觀,從Application調用入口,到斷點MyBeanPostProcessor.postProcessBeforeInitialization中間的所有調用過程都展示出來了,那麼源碼閱讀的話就可以按照這個流程逆向進行分析。
根據調用鏈可以進入AbstractAutowireCapableBeanFactory.initializeBean方法中
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
//略...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//這裏調用了postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//這裏調用了postProcessAfterInitialization
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
- applyBeanPostProcessorsBeforeInitialization
獲取系統中所有的BeanPostProcessor對象,並調用其postProcessBeforeInitialization方法
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;
}
- applyBeanPostProcessorsAfterInitialization
獲取系統中所有的BeanPostProcessor對象,並調用其postProcessAfterInitialization方法
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
getBeanPostProcessors(),這裏就更簡單了,直接獲取到BeanPostProcessor對象集合。
public List<BeanPostProcessor> getBeanPostProcessors() {
return this.beanPostProcessors;
}
那麼是在什麼時候將BeanPostProcessor對象都添加到集合中去的呢?
在Spring初始化完成後會回調ApplicationContext中的refresh方法,在AbstractApplicationContext.refresh()中完成了共通邏輯的實現。源碼如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//略。。。
//從這裏註冊BeanPostProcessors
registerBeanPostProcessors(beanFactory);
//略。。。
}
}
registerBeanPostProcessors這裏非常簡單的調用了PostProcessorRegistrationDelegate,將BeanFactory作爲參數傳入,那麼這裏是不是就是可以猜想出是如何獲取BeanPostProcessor的吧!
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
繼續跟入到registerBeanPostProcessors,這裏的第一句代碼就很清楚的說明了獲取的方式,從Bean工廠中獲取到所有BeanPostProcessor實現類。
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);}
獲取完所有實現類後,就是根據實現類上的順序進行排序,然後將排序好的集合對象調用BeanFactory.addBeanPostProcessor註冊到BeanFactory中。這樣在AbstractAutowireCapableBeanFactory中就可以從它的父類AbstractBeanFactory中直接獲取到BeanPostProcessor集合對象了,也就是上面調用的getBeanPostProcessors()方法。registerBeanPostProcessors方法實現的代碼沒有什麼難度,這裏就不多貼代碼了。
以上代碼從getBean入口到實際調用BeanPostProcessor接口的核心流程分析完畢了。知道 了BeanPostProcessor執行過程,那麼InitializingBean的是何時回調的呢?
AbstractAutowireCapableBeanFactory.initializeBean方法中調用applyBeanPostProcessorsBeforeInitialization方法後,調用了invokeInitMethods方法。從方法的實現代碼可以看出,如果bean實現了InitializingBean接口,就回調afterPropertiesSet方法。
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
這樣BeanPostProcessor和InitializingBean的執行關係如下:
postProcessBeforeInitialization->afterPropertiesSet->postProcessAfterInitialization
在bean加載完成後回調中,還可以使用@PostConstruct實現。能夠實現和
InitializingBean一樣的效果,那麼這裏來看下調用鏈關係:
從上圖可以看出,@PostConstruct是通過反射進行調用的。在InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization通過反射調用方法。源碼如下:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);
}
return bean;
}
這裏可以看出,@PostConstruct其實也是通過BeanPostProcessor機制來實現的,這個可以說是BeanPostProcessor使用的一個非常好的例子。 其中LifecycleMetadata存儲了添加了@PostConstruct註解的所有方法,然後通過反射循環調用所有的對應的方法。這裏的源碼就不繼續深究了,有興趣的可以自行查看。
總結
通過以上的講解,這裏可以得出@PostConstruct,BeanPostProcessor,InitializingBean之間的調用順序:
BeanPostProcessor.postProcessBeforeInitialization->@PostConstruct->InitializingBean->BeanPostProcessor.postProcessAfterInitialization
既然@PostConstruct,BeanPostProcessor,InitializingBean 都可以實現在bean初始化完成後執行特定的操作,至於使用哪種還是看項目的使用習慣了,通常來說InitializingBean是使用最多的,@PostConstruct使用註解的方式來實現的話不夠直觀,對後期維護來說可能不太適合。BeanPostProcessor比較適合在框架類型或者面向特定接口使用。
這裏重點還是BeanPostProcessor,通過這個接口可以進行更多的業務邏輯操作,至於如何取捨那麼就需要看項目的實際情況了。