Spring的後處理器-BeanPostProcessor跟BeanFactoryPostProcessors

        最近在重讀spring源碼(爲何要重讀?由於不得不認可,去年跟着《深刻解析sping源碼》一書過了一遍spring的源碼,除了滿腦殼都是各類BeanFactory跟BeanDefinition外,真的沒什麼收穫...),spring

而第二遍因爲是分模塊分功能點去看,有了針對性,再加上以前囫圇吞棗的那一遍給我帶來的總體認識,這一次順暢了許多,對spring的理解亦深刻了下去。因此說,閱讀源碼真的能帶給人不少的收穫,app

並且不止於代碼。後面會專門找個合適的時機對本身學習以及閱讀源碼這個過程作個總結,今天先聊重點:Spring的後處理器是如何發揮做用的框架

 

        零:什麼是後處理器                                                                                                                                                                                  

        Spring的後處理器只要有兩大類,一個是針對BeanDefinition的容器級別的後處理器 - BeanFactoryPostProcessor(後面簡稱BFPP);一個是針對getBean操做得到的對象的後處理器 -post

BeanPostProcessor(後面簡稱BPP)。學習

         此兩個後處理器不一樣之處主要有三點:this

        一、觸發時機不一樣,前者BFPP是在容器refresh方法中調用,然後者實際調用時機是在getBean方法獲取對象時調用;spa

        二、因觸發時機不一樣致使兩者處理的對象不一樣。BFPP處理的是解析完配置文件後註冊在容器中的BeanDefinition,而BPP處理的是經過反射生成code

的實例Bean;對象

        三、接口樣式不一樣,BFPP只有一個後處理方法,而BPP有一個前置處理方法一個後置處理方法。blog

        接口以下所示:

 1 public interface BeanFactoryPostProcessor {
 2 
 3     void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
 4 
 5 }
 6 
 7 
 8 public interface BeanPostProcessor {
 9 
10     default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
11         return bean;
12     }
13 
14     default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
15         return bean;
16     }
17 
18 }

 

        一:BeanFactoryPostProcessor                                                                                                                                                                    

        在ApplicationContext容器初始化的核心方法refresh方法中,初始化完BeanFactory後會執行invokeBeanFactoryPostProcessors方法,就是在此方法中完成了對BFPP的調用。代碼以下所示:

1 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
2         PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
3 
4         if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
5             beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
6             beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
7         }
8 }

        能夠知道,完成後處理器調用的方法是第二行的代碼。此處請記住這個類PostProcessorRegistrationDelegate,它是處理後處理器的核心類。對於此處的方法invokeBeanFactoryPostProcessors,

代碼很長,就不貼出來了,下面只作一些簡單的梳理。

        一、先判斷當前BeanFactory是否是BeanDefinitionRegistry,若是是則將實現BeanDefinitionRegistryPostProcessor(簡稱BDRPP)接口的類找到,按順序執行後處理器的方法。此BDRPP接口是BFPP

接口的子類。其做用是什麼?爲何要單獨對其進行處理?看其後置方法即可只其一二。它的後置處理方法參數是一個BeanDefinitionRegistry,它是作什麼用的?往容器中註冊BeanDefinition的。因此此

處單獨處理BDRPP的緣由也就基本明瞭了 - 就是根據實際須要在此處建立BeanDefinition往容器中註冊的,即留了一個往容器中註冊bean的後門。

        二、再獲取全部實現了BeanFactoryPostProcessor接口的子類,按照順序執行後處理器方法。

 

        二:BeanPostProcessor                                                                                                                                                                         

        BPP後處理器的處理,相比BFPP多了一步,BFPP是在ApplicationContext容器初始化的時候就調用了,而BPP是在容器初始化時註冊,調用則是在getBean獲取對象實例時觸發。

        一、BPP的註冊

        一樣是在refresh方法中,就在調用BFPP方法的下面,調用了註冊BPP的方法  - registerBeanPostProcessors方法。

1 protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
2        PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
3 }

        看到熟悉的面孔了吧,對,仍是它,那個調用BFPP的類!

        此方法代碼一樣很多,此處就不貼出來了,主要梳理一下代碼邏輯:

        1)、首先是獲取BPP實例。獲取BPP子類的套路跟上文中獲取BFPP子類的套路基本同樣,先經過類的type獲取到註冊到當前容器中的全部的子類,而後根據是否實現了PriorityOrdered、Ordered接口

對其進行分類、排序,最後再經過beanFactory的getBean方法獲取到BeanPostProcessor的實例對象;

        2)、註冊BPP實例。註冊的順序是先註冊實現了PriorityOrdered的實例,再註冊Ordered的實例,最後註冊普通的BPP。此處的註冊是指將這個BPP放入AbstractBeanFactory維護的一個成員變量中,

此變量是一個CopyOnWriteArrayList。在加入以前會先執行一下remove方法,防止重複加入。

 

        二、BPP的調用

         BeanFactory的多個getBean方法,大可能是在AbstractBeanFactory方法中實現,而最終建立bean實例,則是在AbstractAutowireCapableBeanFactory中實現。在此類的doCreateBean方法的initializeBean

方法中,實現了對BPP的調用。方法代碼以下所示:

 1 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
 2         if (System.getSecurityManager() != null) {
 3             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 4                 invokeAwareMethods(beanName, bean);
 5                 return null;
 6             }, getAccessControlContext());
 7         }
 8         else {
 9             invokeAwareMethods(beanName, bean);
10         }
11 
12         Object wrappedBean = bean;
13         if (mbd == null || !mbd.isSynthetic()) {
14             wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
15         }
16 
17         try {
18             invokeInitMethods(beanName, wrappedBean, mbd);
19         }//此處catch異常處理代碼已被BZ去掉
20         
21         if (mbd == null || !mbd.isSynthetic()) {
22             wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
23         }
24 
25         return wrappedBean;
26 }

        由代碼可知,BPP的前置、後置方法,分別在第14行、第22行被調用,返回的結果即建立出來的bean。

 

        三:小結                                                                                                                                                                                                     

         經過這兩大類後處理器,能充分感受到Spring對於框架擴展性作出的努力。後處理器理解實現過程不難,難的是如何在實際工做中靈活的利用spring的這個擴展性解決項目中的問題,或者利用其給

開發帶來效率上的提高。經常使用的場景好比過濾bean的某些內容,對其進行特殊處理(實際工做中BZ也沒用過 ><)。以上就是BZ對spring中後處理器的理解,可能因水平或者思考角度等緣由有理解不到位的

地方,歡迎各位猿友們批評指正!

         就此別過,下期初步打算講講AOP的相關原理。

相關文章
相關標籤/搜索