spring mvc 父子容器AOP-建言者(切面,事務)的繼承
spring 是含有父子容器的機制的,特別是在使用springmvc的情況下,父子容器是非常容易出現的,也是大家常用的。那麼父子容器之間的關係到底是怎麼樣的呢。
之前聽說和看別的文章說,springmvc中父容器事務不起效,不要使用父子容器都載入配置資訊等,這些會在本文中揭開疑惑。
所以提出了幾個疑問,父子容器之間的關係是怎樣生成的,父子容器之間的關係是什麼樣的,父子容器方面有什麼特殊實現呢(特別說明建言者方面的,也是本文的重點) 開頭先時候總結方便解決問題,文章後半部分會結合原始碼解讀原理和機制。
注:統一一下名詞 對於spring來說 切面和事務都是以建言者(Advisor)的方式來實現的
1、父子容器之間的建言者是什麼關係呢
子容器想使用父容器的建言需要幾個條件
(1)子容器必須含有AbstractAutoProxyCreator的子類例項(它是BeanPostProcessor的實現類)(如事務配置,aspectj切面配置等代理配置)
(2)子容器中的AbstractAutoProxyCreator的子類例項的配置必須不是(InfrastructureAdvisorAutoProxyCreator的例項)也就是spring預設名稱為“org.springframework.aop.config.internalAutoProxyCreator“的bean,也就是說如含有的配置檔案必須晚於配置檔案的載入。或者說子容器只有的配置檔案是不行的,必須也要有其他的AbstractAutoProxyCreator的子類例項的配置如並且要早於的配置檔案載入。
(3)子容器中建言者的名稱不能和父容器的建言者名稱相同。
舉個例子
說明 – 沒有說明import就沒有import其他配置檔案。
一、
【1】父容器中配置順序為:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一個切面)
【2】子容器中配置順序為: servlet.xml,aop-config.xml
那麼執行時子容器就會繼承父容器的一個切面加上自已有一個(此時子容器中切面會執行兩次)。
二、
【1】父容器中配置順序為:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一個切面)
【2】子容器中配置順序為: servlet.xml, tx-config.xml
此時子容器中不會繼承父容器的切面。
三、
【1】父容器中配置順序為:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一個切面)
【2】子容器中配置順序為: servlet.xml, tx-config.xml,aop-config.xml
此時子容器中不會繼承父容器的切面。
四、
【1】父容器中配置順序為:applicaitonContext.xml (import aop-config.xml,tx-config.xml)
【2】子容器中配置順序為: servlet.xml, aop-config.xml
此時子容器中會繼承父容器的切面。
例子就舉到這裡,因為場景是無限的,只有原理和機制是一樣的,只要瞭解了原理和機制,我們就不怕各種場景
2、父子容器的關係
前面說了建言者的繼承關係,這裡說下bean的方面的關係。
spring 噹噹前容器不存在當前beanName的配置描述資訊時,會看父容器是否存在,如果當前容器存在父容器並且父容器存在當前beanName的描述資訊,此時會通過父容器的getBean方法獲取父容器中當前bean的例項返回。
也就是說一樣的bean不需要配置兩次,在父子容器都配置,這樣只會浪費記憶體和時間。
3、父子容器是怎麼樣實現的
這裡只針對spring mvc的場景,spring mvc會先載入web.xml中配置的listener 載入的容器這個是父容器,servlet中載入的配置形成的容器這個容器是子容器,生成子容器的時候會把,父容器的引用通過servletconfig中拿出來放入子容器中。
===============原始碼解析分割線=================
看完解決有人會問為什麼,它是怎麼實現的,沒有原始碼我不信,下面咱們一起看看原始碼,看看它是怎麼實現的,原理是什麼
1、父子容器之間的建言者是什麼關係
說到建言者就離不開代理,spring對含有建言者的bean就一定會生成代理物件。
代理的建立機制在這篇文章中有詳解:http://blog.csdn.net/songyang19871115/article/details/54345224
代理的建立者AbstractAutoProxyCreator子類的實現取決於spring配置的載入和原理:http://blog.csdn.net/songyang19871115/article/details/54346444
spring的代理的建立取決於是否含有目標源(targetSource)和建言者。
(圖1.1)
(圖1.2)
從上圖1.1,1.2 原始碼看出 spring在生成代理的時候一定會獲取建言者資訊。
findCandidateAdvisors 呼叫findAdvisorBeans方法下面會詳解。
findAdvisorsThatCanApply 過濾掉當前bean不匹配的建言者資訊extendAdvisors 處理如果包含aspectj 建言者,並且沒有預設建言者則新增一個預設建言者到建言者鏈的頭部。
而且會針對所有建言者進行排序。
(圖1.3)
spring獲取建言者名稱,並且匹配出適合的建言者。
(圖1.4)
如圖可以看出,spring獲取建言者的時候會把當前容器的和父容器的建言者獲取出來,然後匹配當前容器是否含有當前建言者(按名稱),如果不包含則放在集合裡去。也就是會繼承父容器的建言者只要名稱不一致。
(圖1.5)
(圖1.6)
從圖1.6中看出InfrastructureAdvisorAutoProxyCreator 只有這個建言者存在於當前beanFactory中才會是適和當前bean的建言者。
(圖1.7)
圖1.7為DefaultAdvisorAutoProxyCreator中判斷建言者是否適合當前bean,它是通過字首來匹配的。(但是spring沒有使用這個代理建立者,如果二次開發或拓展spring的時候請注意這部分)
而AbstractAdvisorAutoProxyCreator的其他子類:AnnotationAwareAspectJAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator對判斷是否建言者是否適合當前bean都是使用預設提供的方法。如下圖1.8
至此說明這兩種creator都是可以繼承父容器和子容器不同名建言者的。
而使用哪種代理creator取決於配置的載入原理。詳見http://blog.csdn.net/songyang19871115/article/details/54346444
spring提供了一種bean名稱自動生成的實現 如下圖1.1.1
(圖1.1.1)
xmlReaderContext是ReaderContext的唯一實現
bean名稱的生成spring提供了兩種一種是AnnotationBeanNameGenerator註解bean的生成規則,一種是DefaultBeanNameGenerator,
在沒有指定名稱的時候他們會使用對應的規則生成名稱。
(圖1.1.2)
(圖1.1.3)
(圖1.1.4)
從圖1.1.2,1.1.3,1.1.4中可以看出註解的名稱生成規則是通過類名生成的,並且駝峰式命名,如果前兩個字母大寫了就全是大寫。
除了註解以外名稱的命名都是使用DefaultBeanNameGenerator
(圖1.1.5)
(圖1.1.6)
圖1.1.5,圖1.1.6原始碼中顯示預設的名稱實現為類名 + 字尾 + “#” 內部類使用beanDefinition hash code的hex 字串。
如果沒有內部類則使用第幾個編號。從0開始。而且從原始碼中看出判斷是否存在當前bean描述資訊時,只使用了當前容器,那麼使用的一樣的規則(同樣配置方法)就會產生重名。進而影響繼承父容器的建言者。
總結:鑑於上面提到的建言者重名導致的不能被子容器繼承的關係。舉個例子說明:
如果父容器例項了兩個aop建言(配置檔案方式),那麼按照名稱生成規則兩個的名稱為org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,和org.springframework.aop.aspectj.AspectJPointcutAdvisor#1,
如果子容器也例項了肯定也是使用了這套規則,那麼如果子容器配置了一個就會叫做org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,如果只考慮名稱繼承規則,那麼子容器就會繼承org.springframework.aop.aspectj.AspectJPointcutAdvisor#1這個建言者。
2、spring子容器使用父容器bean
(圖2.1)
如上原始碼:spring在發現當前容器中沒有當前bean的時候,不會去先建立而是去父容器獲得,此處使用的是遞迴演算法,也就是當前容器不存在只要任何祖先容器存在都會獲取到。離他越近的祖先容器中有就會使用到。