【Spring註解驅動開發】關於BeanPostProcessor後置處理器,你瞭解多少?
阿新 • • 發佈:2020-06-25
## 寫在前面
> 有些小夥伴問我,學習Spring是不是不用學習到這麼細節的程度啊?感覺這些細節的部分在實際工作中使用不到啊,我到底需不需要學習到這麼細節的程度呢?我的答案是:有必要學習到這麼細節的程度,而且是有機會、有條件一定要學!吃透Spring的原理和原始碼!往往拉開人與人之間差距的就是這些細節的部分,當前只要是使用Java技術棧開發的Web專案,幾乎都會使用Spring框架。而且目前各招聘網站上對於Java開發的要求幾乎清一色的都是熟悉或者精通Spring。所以,你,很有必要學習Spring的細節知識點。
>
> 專案工程原始碼已經提交到GitHub:[https://github.com/sunshinelyz/spring-annotation](https://github.com/sunshinelyz/spring-annotation)
## BeanPostProcessor後置處理器概述
首先,我們來看下BeanPostProcessor的原始碼,看下它到底是個什麼鬼,如下所示。
```java
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
```
從原始碼可以看出:BeanPostProcessor是一個介面,其中有兩個方法,postProcessBeforeInitialization和postProcessAfterInitialization兩個方法,這兩個方法分別是在spring容器中的bean初始化前後執行,所以spring容器中的每一個bean物件初始化前後,都會執行BeanPostProcessor介面的實現類的這兩個方法。
也就是說,**postProcessBeforeInitialization方法會在bean例項化和屬性設定之後,自定義初始化方法之前被呼叫,而postProcessAfterInitialization方法會在自定義初始化方法之後被呼叫。當容器中存在多個BeanPostProcessor的實現類時,會按照它們在容器中註冊的順序執行。對於自定義BeanPostProcessor實現類,還可以讓其實現Ordered介面自定義排序。**
因此我們可以在每個bean物件初始化前後,加上自己的邏輯。實現方式:自定義一個BeanPostProcessor介面的實現類MyBeanPostProcessor,然後在類MyBeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法裡面寫上自己的邏輯。
## BeanPostProcessor後置處理器例項
我們建立一個MyBeanPostProcessor類,實現BeanPostProcessor介面,如下所示。
```java
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 測試BeanPostProcessor
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@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;
}
}
```
接下來,我們執行BeanLifeCircleTest類的testBeanLifeCircle04()方法,輸出的結果資訊如下所示。
```bash
呼叫了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
呼叫了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$e8ab4f2e@56528192
Cat類的構造方法...
呼叫了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的postConstruct()方法...
Cat的init()方法...
呼叫了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@1b1473ab
Cat的preDestroy()方法...
Cat的destroy()方法...
```
可以看到,postProcessBeforeInitialization方法會在bean例項化和屬性設定之後,自定義初始化方法之前被呼叫,而postProcessAfterInitialization方法會在自定義初始化方法之後被呼叫。
也可以讓實現Ordered介面自定義排序,如下所示。
```java
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 測試BeanPostProcessor
*/
@Component
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;
}
}
```
再次執行BeanLifeCircleTest類的testBeanLifeCircle04()方法,輸出的結果資訊如下所示。
```bash
呼叫了postProcessBeforeInitialization方法,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
呼叫了postProcessAfterInitialization,beanName = animalConfig, bean = io.mykit.spring.plugins.register.config.AnimalConfig$$EnhancerBySpringCGLIB$$b045438a@1ed1993a
Cat類的構造方法...
呼叫了postProcessBeforeInitialization方法,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的postConstruct()方法...
Cat的init()方法...
呼叫了postProcessAfterInitialization,beanName = cat, bean = io.mykit.spring.plugins.register.bean.Cat@36c88a32
Cat的preDestroy()方法...
Cat的destroy()方法...
```
## BeanPostProcessor後置處理器作用
後置處理器用於bean物件初始化前後進行邏輯增強。spring提供了BeanPostProcessor的很多實現類,例如AutowiredAnnotationBeanPostProcessor用於@Autowired註解的實現,AnnotationAwareAspectJAutoProxyCreator用於SpringAOP的動態代理等等。
除此之外,我們還可以自定義BeanPostProcessor的實現類,在其中寫入需要的邏輯。下面以AnnotationAwareAspectJAutoProxyCreator為例,說明後置處理器是怎樣工作的。我們都知道springAOP的實現原理是動態代理,最終放入容器的是代理類的物件,而不是bean本身的物件,那麼spring是什麼時候做到這一步的?就是在AnnotationAwareAspectJAutoProxyCreator後置處理器的postProcessAfterInitialization方法,即bean物件初始化完成之後,後置處理器會判斷該bean是否註冊了切面,如果是,則生成代理物件注入容器。Spring中的關鍵程式碼如下所示。
```java
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
```
**好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!**
> 專案工程原始碼已經提交到GitHub:[https://github.com/sunshinelyz/spring-annotation](https://github.com/sunshinelyz/spring-annotation)
## 寫在最後
> 如果覺得文章對你有點幫助,請微信搜尋並關注「 **冰河技術** 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。
部分參考:https://www.cnblogs.com/dubhlinn/p/10668156.