BeanPostProcessor源碼解析
1. 解釋
BeanPostProcessor 就是Bean 的後置處理器 ,主要作用就是 Bean 實例之後,在 initialization 之前和之後 調用自定義的方法 改變一些屬性
這裏 的 initialization 包含: Bean 裏面定義的 initMethod , InitializingBean 的 afterPropertiesSet
此外 還有一個 annotation @PostConstruct 和 @PreDestroy,也是可以對Bean 進行擴展的,但是他們的邏輯 和上面的 InitializingBean 和 自定義 的initMethod 的 底層一點點區別,他們 類似 BeanPostProcessor ,他對應的 類是 CommonAnnotationBeanPostProcessor
看一下 BeanPostProcessor 的接口
我用的 是 spring-beans: 5.1.7.RELEASE 版本,下面兩個方法都加了 default
/**
主要作用就是 :允許對 Bean instances 自定義修改
ApplicationContexts 會自動 檢查到 BeanPostProcessor beans 並將它們應用於隨後創建的任何bean
*/
public interface BeanPostProcessor {
/**
* 在 進行調用 initialization 方法之前 先運行此方法,這裏的 initialization 比如
* (InitializingBean's {@code afterPropertiesSet},或者 or 自定義的 init-method
* 默認是返回Bean 本身
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* 在 進行調用 initialization 方法之後 運行此方法, 這裏的 initialization 比如
* (InitializingBean's {@code afterPropertiesSet},或者 or 自定義的 init-method
* 如果是 FactoryBean, 那麼 此方法不僅作用於FactoryBean 實例,還作用於 此FactoryBean 創建的對象(自從 spring 2.0 ) ,此post-processor 通過 相應的check 可以決定是作用於 FactoryBean 還是 創建的對象,或者兩者
* 默認是返回Bean 本身
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
2. demo
2.1 common Demo
- 創建一個Bean
@Configuration
public class BeanA {
private static final Logger log = LoggerFactory.getLogger(BeanA.class);
public BeanA() {
System.out.println("=======Bean A 構造函數======");
}
}
- 創建一個自定義的BeanPostProcessor
@Component
public class MysqlBeanPostProcessor implements BeanPostProcessor {
public MysqlBeanPostProcessor() {
System.out.println("=======run MysqlBeanPostProcessor 構造函數====");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BeanA) {
// 這裏我們可以對bean 做一下擴展
System.out.println("=====before Initialization 運行");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BeanA) {
System.out.println("=====after Initialization 運行");
}
return bean;
}
}
- 弄個啓動類 運行一下
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args);
}
}
- 運行結果:
1.可以看到 是先注入我們自定義的MysqlBeanPostProcessor ,然後作用於每一個Bean - 先在Bean 實例之後,然後先運行before ,再運行 after 方法.(這個不夠明朗,弄一下下面一個例子)
=======run MysqlBeanPostProcessor 構造函數====
INFO 3360 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
INFO 3360 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
INFO 3360 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 3360 --- [ main] o.a.catalina.core.AprLifecycleListener : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 3360 --- [ main] o.a.catalina.core.AprLifecycleListener : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 3360 --- [ main] o.a.catalina.core.AprLifecycleListener : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 3360 --- [ main] o.a.catalina.core.AprLifecycleListener : OpenSSL successfully initialized [OpenSSL 1.1.1c 28 May 2019]
INFO 3360 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
INFO 3360 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1135 ms
=======Bean A 構造函數======
=====before Initialization 運行
=====after Initialization 運行
2.2 demo 增加 InitializingBean
將上面的BeanA 方法 實現一下 InitializingBean
@Configuration
public class BeanA implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BeanA.class);
public BeanA() {
System.out.println("=======Bean A 構造函數======");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("=======Bean A 運行 InitializingBean 的 afterPropertiesSet======");
}
}
運行結果:
=======run MysqlBeanPostProcessor 構造函數====
INFO 4200 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
INFO 4200 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
INFO 4200 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 4200 --- [ main] o.a.catalina.core.AprLifecycleListener : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 4200 --- [ main] o.a.catalina.core.AprLifecycleListener : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 4200 --- [ main] o.a.catalina.core.AprLifecycleListener : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 4200 --- [ main] o.a.catalina.core.AprLifecycleListener : OpenSSL successfully initialized [OpenSSL 1.1.1c 28 May 2019]
INFO 4200 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
INFO 4200 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1093 ms
=======Bean A 構造函數======
=====before Initialization 運行
=======Bean A 運行 InitializingBean 的 afterPropertiesSet======
=====after Initialization 運行
2.3 demo 增加 @PostConstruct
修改 BeanA 代碼如下:
@Configuration
public class BeanA implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BeanA.class);
public BeanA() {
System.out.println("=======Bean A 構造函數======");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("=======Bean A 運行 InitializingBean 的 afterPropertiesSet======");
}
@PostConstruct
public void init(){
System.out.println("=======Bean A 運行 PostConstruct 的 初始化======");
}
}
運行結果如下:
說明 beforxxx 、 afterxxx、 方法是在 @PostConstruct 前後運行
=======run MysqlBeanPostProcessor 構造函數====
INFO 22072 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
INFO 22072 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
INFO 22072 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 22072 --- [ main] o.a.catalina.core.AprLifecycleListener : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 22072 --- [ main] o.a.catalina.core.AprLifecycleListener : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 22072 --- [ main] o.a.catalina.core.AprLifecycleListener : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 22072 --- [ main] o.a.catalina.core.AprLifecycleListener : OpenSSL successfully initialized [OpenSSL 1.1.1c 28 May 2019]
INFO 22072 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
INFO 22072 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1325 ms
=======Bean A 構造函數======
=====before Initialization 運行
=======Bean A 運行 PostConstruct 的 初始化======
=======Bean A 運行 InitializingBean 的 afterPropertiesSet======
=====after Initialization 運行
3. 源碼分析
3.1 運行時機
接下來我們 看一下如何運行的,以及分析一下源碼,在 打印日誌的地方 打個斷點,Debug 一下,如下圖:
從下面我們可以看到
我們從上面的左下面的框框裏面可以看到 這裏是在 運行refresh() 裏面方法時觸發的,refresh這塊後續整理一下//TODO,本次我們就從 AbstractAutowireCapableBeanFactory 類的 initializeBean 方法 開始解析
這裏的流程也很清晰,大致爲:
1.首先判斷 是否設置了SecurityManagers ,如果設置了,就進行相關的權限配置 和 Aware 的擴展
2. 如果沒有 直接進行 Aware 的擴展 ,Aware 這塊就是 對相關的Bean 額外的配置一些響應的屬性 ,代碼塊如下//TODO 後續整理一下
3. 判斷bean 是不是應用程序自己定義的,如果不是 ,那就 遍歷 運行 BeanPostProcessors 的postProcessBeforeInitialization 方法 這裏有一個 getBeanPostProcessors() 方法,裏面是獲取所有的 實現了BeanPostProcessors 接口的類,這裏是如果獲取到的呢,在何時放進去的呢,下面會提到.
上面說到 的 註解 @PostConstruct 是在這一步運行的 ,相當於 BeanPostProcessors 的Beforxxxx 方法
4. 運行 invokeInitMethods ,這裏有兩種 ,一種是 繼承了 InitializingBean ,那就實現 對應的afterPropertiesSet() 方法,或者是自定義的 InitMethod ,通過反射 去調用 上面的InitializingBean 和 自定義的initMethod 是在這一步運行
5. 判斷bean 是不是應用程序自己定義的,如果不是 ,那就 遍歷 運行 BeanPostProcessors 的postProcessAfterInitialization @PreDestory是在這一步 完成
// 這裏就是對Bean 進行屬性的配置
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
// 這裏就是 遍歷的去運行 BeanPostProcessors 的postProcessBeforeInitialization 方法
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
3.2 getBeanPostProcessors()
上面提到 getBeanPostProcessors()裏面的值是何時放進去的,其實 是在 refresh() 方法裏面的 registerBeanPostProcessors(beanFactory) 裏面進行收集的.