Spring注解驅(qū)動之BeanPostProcessor后置處理器講解
概述
在學習Spring的時候,在了解基本用法的時候,如果有時間一定要深入源碼了解Spring的底層原理,這樣在做一些適配工作、寫一些輪子的時候就會比較容易,否則會很難,甚至一頭霧水,無法完成工作。
吃透Spring的原理和源碼,往往可以拉開人們之間的差距,當前只要是使用Java技術(shù)棧開發(fā)的Web項目,幾乎都會使用Spring框架。
而且目前各招聘網(wǎng)站上對于Java開發(fā)的要求幾乎清一色的都是熟悉或者精通Spring,所以,你很有必要學習Spring的細節(jié)知識點。
BeanPostProcessor后置處理器概述
首先,看下BeanPostProcessor的源碼。
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
從源碼可以看出,BeanPostProcessor是一個接口,其中有兩個方法:
postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法分別是在Spring容器中的bean初始化前后執(zhí)行,所以Spring容器中的每個bean對象初始化前后,都會執(zhí)行BeanPostProcessor接口的兩個方法。
也就是說,postProcessBeforeInitialization方法會在bean實例化和屬性設(shè)置之后,自定義初始化方法之前被調(diào)用,而postProcessAfterInitialization方法會在自定義初始化方法之后被調(diào)用。當容器中存在多個BeanPostProcessor的實現(xiàn)類時,會按照它們在容器中注冊的順序執(zhí)行。對于自定義的BeanPostProcessor實現(xiàn)類,還可以讓其實現(xiàn)Ordered接口自定義排序。
因此我們可以在每個bean對象初始化前后,加上自己的邏輯。實現(xiàn)方式是自定義一個BeanPostProcessor接口的實現(xiàn)類,例如MyBeanPostProcessor,然后在該類的postProcessBeforeInitialization和postProcessAfterInitialization這兩個方法寫上自定義邏輯。
BeanPostProcessor后置處理器實例
我們創(chuàng)建一個MyBeanPostProcessor類,實現(xiàn)BeanPostProcessor接口,如下所示。
package com.meimeixia.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了,否則無法工作
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
}
接下來,我們應(yīng)該是要編寫測試用例來進行測試了。
package com.meimeixia.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.meimeixia.bean.Car;
@ComponentScan("com.meimeixia.bean")
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod="init", destroyMethod="destroy")
public Car car() {
return new Car();
}
}
第二處改動是將Cat類上添加的@Scope(“prototype”)注解給注釋掉,因為咱們之前做測試的時候,也是將Cat對象設(shè)置成多實例bean了。
package com.meimeixia.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
// @Scope("prototype")
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor...");
}
/**
* 會在容器關(guān)閉的時候進行調(diào)用
*/
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat destroy...");
}
/**
* 會在bean創(chuàng)建完成,并且屬性都賦好值以后進行調(diào)用
*/
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat afterPropertiesSet...");
}
}
好了,現(xiàn)在咱們就可以編寫測試用例來進行測試了。
可喜的是,我們也不用再編寫一個測試用例了,直接運行IOCTest_LifeCycle類中的test01()方法就行,該方法的代碼如下所示。
@Test
public void test01() {
// 1. 創(chuàng)建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器創(chuàng)建完成");
// 關(guān)閉容器
applicationContext.close();
}
此時,運行IOCTest_LifeCycle類中的test01()方法,輸出的結(jié)果信息如下所示。

可以看到,postProcessBeforeInitialization方法會在bean實例化和屬性設(shè)置之后,自定義初始化方法之前被調(diào)用,而postProcessAfterInitialization方法會在自定義初始化方法之后被調(diào)用。
當然了,也可以讓我們自己寫的MyBeanPostProcessor類來實現(xiàn)Ordered接口自定義排序,如下所示。
package com.meimeixia.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 后置處理器,在初始化前后進行處理工作
* @author liayun
*
*/
@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public int getOrder() {
return 3;
}
}
BeanPostProcessor后置處理器作用
后置處理器可用于bean對象初始化前后進行邏輯增強。
Spring提供了BeanPostProcessor接口的很多實現(xiàn)類,例如AutowiredAnnotationBeanPostProcessor用于@Autowired注解的實現(xiàn),AnnotationAwareAspectJAutoProxyCreator用于Spring AOP的動態(tài)代理等等。
除此之外,我們還可以自定義BeanPostProcessor接口的實現(xiàn)類,在其中寫入咱們需要的邏輯。
bean的初始化和銷毀流程
我們知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被調(diào)用;而postProcessAfterInitialization()方法是在bean初始化的之后被調(diào)用。
并且bean的初始化和銷毀方法我們可以通過如下方式進行指定。
1. 通過@Bean指定init-method和destroy-method
@Bean(initMethod="init", destroyMethod="destroy")
2. 通過讓bean實現(xiàn)InitializingBean和DisposableBean這倆接口
@Componentpublic class Cat implements InitializingBean, DisposableBean {}3. 使用JSR-250規(guī)范里面定義的@PostConstruct和@PreDestroy這倆注解
@PostConstruct:在bean創(chuàng)建完成并且屬性賦值完成之后,來執(zhí)行初始化方法@PreDestroy:在容器銷毀bean之前通知我們進行清理工作
4. 通過讓bean實現(xiàn)BeanPostProcessor接口
@Component // 將后置處理器加入到容器中,這樣的話,Spring就能讓它工作了
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {}通過以上四種方式就可以對bean的整個生命周期進行控制:
- bean的實例化:調(diào)用bean的構(gòu)造方法,我們可以在bean的無參構(gòu)造方法中執(zhí)行相應(yīng)的邏輯。
- bean的初始化:在初始化時可以通過BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法進行攔截,執(zhí)行自定義的邏輯。通過@PostConstruct注解、InitializingBean和init-method來指定bean初始化前后執(zhí)行的方法,在該方法中可以執(zhí)行自定義的邏輯。
- bean的銷毀:可以通過@PreDestroy注解、DisposableBean和destroy-method來指定bean在銷毀前執(zhí)行的方法,在方法中可以執(zhí)行自定義的邏輯。
所以,通過上述4種方式,我們可以控制Spring中bean的整個生命周期。
BeanPostProcessor源碼解析
如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相關(guān)的源碼,我們可以在MyBeanPostProcessor類的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法這兩處打上斷點來進行調(diào)試,如下所示。
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization" + ",beanName:"+beanName + ",bean=>" + bean);
return bean;
}
@Override
public int getOrder() {
return 3;
}
}
通過斷點調(diào)試,我們可以看到,在applyBeanPostProcessorBeforeInitialization()方法中,會遍歷所有BeanPostProcessor對象,然后依次執(zhí)行所有BeanPostProcessor對象的postProcessorBeforeInitialization()方法,一旦BeanPostProcessor對象的postProcessBeforeInitialization()方法返回null以后,則后面的BeanPostProcessor對象便不再執(zhí)行了,而是直接退出for循環(huán)。這些都是我們看源碼看到的。
看Spring源碼,我們還看到一個細節(jié),在Spring中調(diào)用initializeBean()方法之前,還調(diào)用了populateBean()方法來為bean的屬性賦值。
經(jīng)過一系列的跟蹤源碼分析,我們可以將關(guān)鍵代碼的調(diào)用過程使用如下偽代碼表述出來。
populateBean(beanName, mbd, instanceWrapper); // 給bean進行屬性賦值
initializeBean(beanName, exposedObject, mbd)
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd); // 執(zhí)行自定義初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
也就是說,在Spring中,調(diào)用initializeBean()方法之前,調(diào)用了populateBean()方法為bean的屬性賦值,為bean的屬性賦好值之后,再調(diào)用initializeBean()方法進行初始化。
在initializeBean()中,調(diào)用自定義的初始化方法(即invokeInitMethods())之前,調(diào)用了applyBeanPostProcessorsBeforeInitialization()方法,而在調(diào)用自定義的初始化方法之后,又調(diào)用了applyBeanPostProcessorsAfterInitialization()方法。至此,整個bean的初始化過程就這樣結(jié)束了。
BeanPostProcessor接口在Spring底層的應(yīng)用案例
ApplicationContextAwareProcessor類
org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的一個實現(xiàn)類,這個類的作用是可以向組件中注入IOC容器,大致的源碼如下。

注意:我這里的Spring版本為4.3.12.RELEASE。
那具體如何使用ApplicationContextAwareProcessor類向組件中注入IOC容器呢?
如果需要向組件中注入IOC容器,那么可以讓組件實現(xiàn)ApplicationContextAware接口。
例如,我們創(chuàng)建一個創(chuàng)建一個Dog類,使其實現(xiàn)ApplicationContextAware接口,此時,我們需要實現(xiàn)ApplicationContextAware接口中的setApplicationContext()方法,在setApplicationContext()方法中有一個ApplicationContext類型的參數(shù),這個就是IOC容器對象,我們可以在Dog類中定義一個ApplicationContext類型的成員變量,然后在setApplicationContext()方法中為這個成員變量賦值,此時就可以在Dog類中的
其他方法中使用ApplicationContext對象了,如下所示。
package com.meimeixia.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* ApplicationContextAwareProcessor這個類的作用是可以幫我們在組件里面注入IOC容器,
* 怎么注入呢?我們想要IOC容器的話,比如我們這個Dog組件,只需要實現(xiàn)ApplicationContextAware接口就行
*
*/
@Component
public class Dog implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Dog() {
System.out.println("dog constructor...");
}
// 在對象創(chuàng)建完成并且屬性賦值完成之后調(diào)用
@PostConstruct
public void init() {
System.out.println("dog...@PostConstruct...");
}
// 在容器銷毀(移除)對象之前調(diào)用
@PreDestroy
public void destory() {
System.out.println("dog...@PreDestroy...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 在這兒打個斷點調(diào)試一下
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}
看到這里,相信不少小伙伴們都有一種很熟悉的感覺,沒錯,我之前也在項目中使用過!是的,這就是BeanPostProcessor在Spring底層的一種使用場景。至于上面的案例代碼為何會在setApplicationContext()方法中獲取到ApplicationContext對象,這就是ApplicationContextAwareProcessor類的功勞了!
接下來,我們就深入分析下ApplicationContextAwareProcessor類。
我們先來看下ApplicationContextAwareProcessor類中對于postProcessBeforeInitialization()方法的實現(xiàn),如下所示。

在bean初始化之前,首先對當前bean的類型進行判斷,如果當前bean的類型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,那么直接返回bean。
如果是上面類型中的一種類型,那么最終會調(diào)用invokeAwareInterfaces()方法,并將bean傳遞給該方法。
invokeAwareInterfaces()方法又是個什么呢?我們繼續(xù)看invokeAwareInterfaces()方法的源碼,如下所示。

可以看到invokeAwareInterfaces()方法的源碼比較簡單,就是判斷當前bean屬于哪種接口類型,然后將bean強轉(zhuǎn)為哪種接口類型的對象,接著調(diào)用接口中的方法,將相應(yīng)的參數(shù)傳遞到接口的方法中。
我們可以看到,此時會將this.applicationContext傳遞到ApplicationContextAware接口的setApplicationContext()方法中。所以,我們在Dog類的setApplicationContext()方法中就可以直接接收到ApplicationContext對象了。
BeanValidationPostProcessor類
org.springframework.validation.beanvalidation.BeanValidationPostProcessor類注意是用來為bean進行校驗操作的,當我們創(chuàng)建bean,并為bean賦值后,我們可以通過BeanValidationPostProcessor類為bean進行校驗操作。BeanValidationPostProcessor類源碼如下:

這里,我們也來看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的實現(xiàn),如下所示。

InitDestroyAnnotationBeanPostProcessor類
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor類主要用來處理@PostConstruct注解和@PreDestroy注解。
使用了@PostConstruct注解和@PreDestroy注解來標注方法,Spring怎么就知道什么時候執(zhí)行@PostConstruct注解標注的方法,什么時候執(zhí)行@PreDestroy注解標注的方法呢?這就要歸功于InitDestroyAnnotationBeanPostProcessor類了。
接下來,我們也通過Debug的方式來跟進下代碼的執(zhí)行流程。首先,在Dog類的initt()方法上打上一個斷點,如下所示。

在InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法中,首先會找到bean中有關(guān)生命周期的注解,比如@PostConstruct注解等,找到這些注解之后,則將這些信息賦值給LifecycleMetadata類型的變量metadata,之后調(diào)用metadata的invokeInitMethods()方法,通過反射來調(diào)用標注了@PostConstruct注解的方法。
這就是為什么標注了@PostConstruct注解的方法會被Spring執(zhí)行的原因。
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, "Failed to invoke init method", ex);
}
return bean;
}
AutowiredAnnotationBeanPostProcessor類
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor類主要是用于處理標注了@Autowired注解的變量或方法。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Spring BeanPostProcessor接口使用詳解
- Spring中的后置處理器BeanPostProcessor詳解
- SpringBoot之通過BeanPostProcessor動態(tài)注入ID生成器案例詳解
- Spring BeanPostProcessor(后置處理器)的用法
- Spring?BeanPostProcessor后處理器源碼解析
- 詳解使用Spring的BeanPostProcessor優(yōu)雅的實現(xiàn)工廠模式
- Spring探秘之如何妙用BeanPostProcessor
- Spring源碼解析之BeanPostProcessor知識總結(jié)
- Spring BeanPostProcessor源碼示例解析
- Spring組件初始化擴展點:BeanPostProcessor
相關(guān)文章
Java報錯:UnsupportedOperationException in Collection
在Java編程中,UnsupportedOperationException是一種常見的運行時異常,通常在試圖對不支持的操作執(zhí)行修改時發(fā)生,它表示當前操作不被支持,本文將深入探討UnsupportedOperationException的產(chǎn)生原因,并提供具體的解決方案和最佳實踐,需要的朋友可以參考下2024-06-06

