【課程免費分享】2-如何在Bean初始化回調前後進行自定義操作

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運行後可得到如下

enter image description here

是不是非常直觀,從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一樣的效果,那麼這裏來看下調用鏈關係:
enter image description here

從上圖可以看出,@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,通過這個接口可以進行更多的業務邏輯操作,至於如何取捨那麼就需要看項目的實際情況了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章