作用
spring具有很好的擴(kuò)展性凌受,但是這個(gè)擴(kuò)展它的這個(gè)擴(kuò)展性體現(xiàn)在哪里呢旅赢?而我們要說的BeanPostProcessor就是對(duì)Spring擴(kuò)展性優(yōu)秀的表現(xiàn)之一。
簡單的說就是BeanPostProcessor提供了初始化前后回調(diào)的方法放椰,我們所說的擴(kuò)展就是在實(shí)例化前后對(duì)Bean進(jìn)行擴(kuò)展缔刹。
接口定義
public interface BeanPostProcessor {
/**
* 初始前調(diào)用
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* 初始化后調(diào)用
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
上面就是BeanPostProcessor接口的定義,從方法名字也能看出這兩個(gè)方法一個(gè)在初始化前調(diào)用一個(gè)在初始化后調(diào)用杀迹。需要注意的是,方法的返回值為原始實(shí)例或者包裝后的實(shí)例押搪。如果返回null會(huì)導(dǎo)致后續(xù)的BeanPostProcessor不生效(BeanPostProcessor是可以注冊(cè)多個(gè)的)。
如何使用
BeanPostProcessorDemo代碼如下:
public class BeanPostProcessorDemo {
public static void main(String[] args) {
//創(chuàng)建基礎(chǔ)容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//加載xml配置文件
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring-bean-post-processor.xml");
//添加BeanPostProcessor
beanFactory.addBeanPostProcessor(new UserBeanPostProcessor());
User user = beanFactory.getBean(User.class);
System.out.println(user);
}
}
@Data
class User{
private String userName;
private Integer age;
private String beforeMessage;
private String afterMessage;
public void initMethod(){
System.out.println("初始化:"+this);
this.setUserName("小明");
this.setAge(18);
}
}
class UserBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User){
System.out.println("初始化前:"+bean);
((User) bean).setBeforeMessage("初始化前信息");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User){
System.out.println("初始化后:"+bean);
((User) bean).setAfterMessage("初始化后信息");
}
return bean;
}
}
spring-bean-post-processor.xml配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.buydeem.beanpostprocessor.User" init-method="initMethod"/>
</beans>
運(yùn)行之后打印結(jié)果如下:
初始化前:User(userName=null, age=null, beforeMessage=null, afterMessage=null)
初始化:User(userName=null, age=null, beforeMessage=初始化前信息, afterMessage=null)
初始化后:User(userName=小明, age=18, beforeMessage=初始化前信息, afterMessage=null)
User(userName=小明, age=18, beforeMessage=初始化前信息, afterMessage=初始化后信息)
上面的代碼很簡單就是創(chuàng)建基礎(chǔ)的容器,因?yàn)槲疫@個(gè)里面用的是BeanFactory影暴,BeanFactory作為基礎(chǔ)容器是需要手動(dòng)將BeanPostProcessor注冊(cè)到容器中去的襟己。下面分析打印結(jié)果:
初始化前:User(userName=null, age=null, beforeMessage=null, afterMessage=null)
該結(jié)果是postProcessBeforeInitialization方法中輸出的內(nèi)容,這個(gè)時(shí)候User實(shí)例還只是進(jìn)行了實(shí)例化厦画,還未進(jìn)行到初始化步驟疮茄,所以所有的屬性都為null,說明該方法確實(shí)是初始化執(zhí)行的。
初始化:User(userName=null, age=null, beforeMessage=初始化前信息, afterMessage=null)
該結(jié)果為自定義的初始化方法initMethod方法中輸出的內(nèi)容根暑,這個(gè)時(shí)候User實(shí)例真正初始化力试,而beforeMessage中中的值正是我們?cè)?strong>postProcessBeforeInitialization設(shè)置的。
初始化后:User(userName=小明, age=18, beforeMessage=初始化前信息, afterMessage=null)
該結(jié)果是postProcessAfterInitialization中輸出內(nèi)容排嫌,從打印結(jié)果可以看出它的確是在自定義initMethod后畸裳。
Spring相關(guān)源碼解讀
如果之前看過了解過Bean的生命周期一定知道,Spring中Bean總體上來說可以分為四個(gè)周期:實(shí)例化淳地、屬性賦值怖糊、初始化、銷毀颇象。而BeanPostProcessor則是在初始化階段的前后執(zhí)行伍伤,下面我通過源碼來說明。
首先看AbstractAutowireCapableBeanFactory中doCreateBean方法遣钳,該方法實(shí)際就是創(chuàng)建指定Bean的方法扰魂。源碼太長這里就不展示了,我們只需要找到其中三個(gè)重要的方法調(diào)用如下:createBeanInstance耍贾、populateBean阅爽、initializeBean。這三個(gè)方法分別代表了Spring Bean中的實(shí)例化荐开、屬性賦值和初始化三個(gè)生命周期付翁。而BeanPostProcessor是在初始化前后調(diào)用,所以我們查看initializeBean中的方法詳情即可晃听。該方法詳情如下:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//處理BeanNameAware百侧、BeanClassLoaderAware砰识、BeanFactoryAware
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
//處理BeanPostProcessor
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//回調(diào)postProcessBeforeInitialization方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//處理InitializingBean和BeanDefinition中指定的initMethod
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()) {
//回調(diào)postProcessAfterInitialization方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
從上面的源碼可以看出首先是處理部分Aware相關(guān)接口,然后接著就是處理BeanPostProcessor中的postProcessBeforeInitialization方法佣渴,該方法詳情如下:
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
//依次處理BeanPostProcessor
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
//如果放回null,則直接返回后續(xù)BeanPostProcessor中的postProcessBeforeInitialization不再執(zhí)行
if (current == null) {
return result;
}
result = current;
}
return result;
}
該方法就是執(zhí)行postProcessBeforeInitialization回調(diào)的詳情內(nèi)容辫狼,從該實(shí)現(xiàn)可以知道,BeanPostProcessor可以有多個(gè)辛润,而且會(huì)按照順序依次處理膨处。如果只要其中的任意一個(gè)返回null,則后續(xù)的BeanPostProcessor的postProcessBeforeInitialization將不會(huì)再處理了砂竖。接著就是執(zhí)行初始化方法真椿,即invokeInitMethods方法被調(diào)用。
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//如果當(dāng)前Bean實(shí)現(xiàn)了InitializingBean接口則會(huì)執(zhí)行它的afterPropertiesSet()方法
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
//如果在BeanDefinition中定義了initMethod則執(zhí)行初始化方法
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
從上面代碼也進(jìn)一步驗(yàn)證了BeanPostProcessor中的postProcessBeforeInitialization方法的確是在初始化前調(diào)用乎澄。當(dāng)invokeInitMethods方法執(zhí)行完之后接著就執(zhí)行applyBeanPostProcessorsAfterInitialization方法突硝。
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
該方法與applyBeanPostProcessorsBeforeInitialization幾乎就是相同的,不同的在于它執(zhí)行的是postProcessAfterInitialization置济。至此Spring Bean的初始化也就完成了解恰。
Spring對(duì)@PostConstruct的支持
通過上面源碼解讀了解了Spring Bean生命周期中初始化的過程,但是實(shí)際上Spring對(duì)于JSR250也支持浙于,例如對(duì)@PostConstruct注解的支持护盈,但是在之前的源碼中并沒有發(fā)現(xiàn)Spring Bean的初始化過程中有所體現(xiàn)。這里面的秘密就是我們的BeanPostProcessor了路媚。在Spring中有一個(gè)CommonAnnotationBeanPostProcessor類黄琼,這個(gè)類的注釋中有說到這個(gè)類就是用來對(duì)JSR250及其他一些規(guī)范的支持。下面我就通過這個(gè)類的源碼來說明Spring是如何通過BeanPostProcessor來實(shí)現(xiàn)對(duì)@PostContruct的支持整慎。
從上圖中我們可以看出脏款,CommonAnnotationBeanPostProcessor并沒有直接對(duì)BeanPostProcessor有所實(shí)現(xiàn),它繼承InitDestroyAnnotationBeanPostProcessor該類裤园,而對(duì)@PostConstruct的實(shí)現(xiàn)主要在該類中撤师。
而對(duì)BeanPostProcessor的實(shí)現(xiàn)代碼如下:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//生命周期元數(shù)據(jù)封裝
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
//執(zhí)行InitMethods
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, "Failed to invoke init method", ex);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
對(duì)BeanPostProcessor的實(shí)現(xiàn)主要在before方法中,該方法主要就是兩部分內(nèi)容拧揽,第一部分主要是信息封裝到LifecycleMetadata中剃盾,便于后面第二步的執(zhí)行相關(guān)初始化方法。
通過上面的方法實(shí)現(xiàn)我們知道了淤袜,Spring對(duì)JSR250的實(shí)現(xiàn)就是借助于BeanPostProcessor來實(shí)現(xiàn)的痒谴。下面我直接用代碼來驗(yàn)證看看我的猜想是否正確。
public class BeanPostProcessorDemo2 {
public static void main(String[] args) {
//創(chuàng)建基礎(chǔ)容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//構(gòu)建BeanDefinition并注冊(cè)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
.getBeanDefinition();
beanFactory.registerBeanDefinition("person",beanDefinition);
//注冊(cè)CommonAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor = new CommonAnnotationBeanPostProcessor();
beanFactory.addBeanPostProcessor(commonAnnotationBeanPostProcessor);
//獲取Bean
Person person = beanFactory.getBean(Person.class);
System.out.println(person);
}
}
class Person{
@PostConstruct
public void annotationInitMethod(){
System.out.println("@PostConstruct");
}
}
上面的代碼比較簡單铡羡,我們定義一個(gè)Person并使用@PostConstruct標(biāo)記出它的初始化方法积蔚,然后我們創(chuàng)建BeanFactory,并創(chuàng)建Person的BeanDefinition將其注冊(cè)到BeanFactory(與讀取配置文件一樣)烦周,然后我們創(chuàng)建CommonAnnotationBeanPostProcessor并將其添加到BeanFactory中尽爆。最后打印結(jié)果打印出@PostConstruct怎顾。如果我們將下面這句代碼注釋。
beanFactory.addBeanPostProcessor(commonAnnotationBeanPostProcessor);
再次執(zhí)行可以發(fā)現(xiàn)漱贱,@PostConstruct將會(huì)失效槐雾,且最后不會(huì)打印出結(jié)果。
使用中的注意事項(xiàng)
如何使用以及實(shí)現(xiàn)原理上面已經(jīng)說了很多幅狮,但是在使用時(shí)有些地方是有限制的募强,對(duì)于這些限制還是要做了解,避免到時(shí)候使用時(shí)出現(xiàn)問題而不知道是什么原因?qū)е碌摹?/p>
順序性
BeanPostProcessor是可以注冊(cè)多個(gè)的彪笼,在AbstractBeanFactory內(nèi)部通過List變量beanPostProcessors來存儲(chǔ)BeanPostProcessor钻注。而在執(zhí)行時(shí)是按照List中BeanPostProcessor的順序一個(gè)個(gè)執(zhí)行的,所以我們?cè)谙肴萜髦刑砑覤eanPostProcessor時(shí)需要注意順序配猫。如果我們不是通過手動(dòng)添加(大多數(shù)時(shí)候不是)時(shí),而是在代碼或者配置文件中定義多個(gè)BeanPostProcessor時(shí)杏死,我們可以通過實(shí)現(xiàn)Ordered接口來控制它的順序泵肄。
BeanPostProcessor依賴的Bean不會(huì)執(zhí)行BeanPostProcessor
BeanPostProcessor依賴的Bean是不會(huì)執(zhí)行BeanPostProcessor的,這是因?yàn)樵趧?chuàng)建BeanPOstProcessor之所依賴的Bean就需要完成初始化淑翼,而這個(gè)時(shí)候BeanPostProcessor都還未完初始化完成腐巢。實(shí)例代碼如下:
public class App3 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.buydeem.beanpostprocessor");
context.register(App3.class);
context.refresh();
}
}
@Component
class ClassA{
}
@Component
class ClassB{
}
@Component
class MyBeanPostProcessor implements BeanPostProcessor{
@Autowired
private ClassA classA;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor"+bean);
return bean;
}
}
最后ClassA是不會(huì)打印出來的,而ClassB是會(huì)被打印出來玄括。因?yàn)镸yBeanPostProcessor依賴ClassA實(shí)例冯丙。
注冊(cè)方式
注冊(cè)BeanPostProcessor方式主要有兩種,一種是在Java或則xml文件配置和聲明作為普通Bean遭京。這樣在ApplicationContext加載時(shí)會(huì)對(duì)這類型的Bean特殊處理胃惜,將其注冊(cè)到容器中來。另外一種則是通過API手動(dòng)注冊(cè)到容器哪雕,就如我們之前的示例一樣船殉,在還未注冊(cè)到容器之前是不會(huì)生效的。
總結(jié)
在Spring中BeanPostProcessor的子接口或?qū)崿F(xiàn)類有很多種斯嚎,例如:InstantiationAwareBeanPostProcessor利虫、MergedBeanDefinitionPostProcessor、DestructionAwareBeanPostProcessor等等堡僻。這些接口分別處在Spring Bean生命周期的不同階段糠惫,而他們的功能與BeanPostProcessor都類似,都是為了給Spring Bean各個(gè)聲明周期提供擴(kuò)展點(diǎn)钉疫。