最近在重讀spring源碼(為什么要重讀?因為不得不承認,去年跟着《深入解析sping源碼》一書過了一遍spring的源碼,除了滿腦袋都是各種BeanFactory跟BeanDefinition外,真的沒什么收獲...),
而第二遍由於是分模塊分功能點去看,有了針對性,再加上之前囫圇吞棗的那一遍給我帶來的整體認識,這一次順暢了許多,對spring的理解亦深入了下去。所以說,閱讀源碼真的能帶給人很多的收獲,
而且不止於代碼。后面會專門找個合適的時機對自己學習以及閱讀源碼這個過程做個總結,今天先聊重點:Spring的后處理器是如何發揮作用的。
零:什么是后處理器
Spring的后處理器只要有兩大類,一個是針對BeanDefinition的容器級別的后處理器 - BeanFactoryPostProcessor(后面簡稱BFPP);一個是針對getBean操作獲得的對象的后處理器 -
BeanPostProcessor(后面簡稱BPP)。
此兩個后處理器不同之處主要有三點:
1、觸發時機不同,前者BFPP是在容器refresh方法中調用,而后者實際調用時機是在getBean方法獲取對象時調用;
2、因觸發時機不同導致二者處理的對象不同。BFPP處理的是解析完配置文件后注冊在容器中的BeanDefinition,而BPP處理的是通過反射生成
的實例Bean;
3、接口樣式不同,BFPP只有一個后處理方法,而BPP有一個前置處理方法一個后置處理方法。
接口如下所示:
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,
代碼很長,就不貼出來了,下面只做一些簡單的梳理。
1、先判斷當前BeanFactory是不是BeanDefinitionRegistry,如果是則將實現BeanDefinitionRegistryPostProcessor(簡稱BDRPP)接口的類找到,按順序執行后處理器的方法。此BDRPP接口是BFPP
接口的子類。其作用是什么?為什么要單獨對其進行處理?看其后置方法便可只其一二。它的后置處理方法參數是一個BeanDefinitionRegistry,它是做什么用的?往容器中注冊BeanDefinition的。所以此
處單獨處理BDRPP的原因也就基本明了了 - 就是根據實際需要在此處創建BeanDefinition往容器中注冊的,即留了一個往容器中注冊bean的后門。
2、再獲取所有實現了BeanFactoryPostProcessor接口的子類,按照順序執行后處理器方法。
二:BeanPostProcessor
BPP后處理器的處理,相比BFPP多了一步,BFPP是在ApplicationContext容器初始化的時候就調用了,而BPP是在容器初始化時注冊,調用則是在getBean獲取對象實例時觸發。
1、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方法,防止重復加入。
2、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的相關原理。