從 spring-aop 通過getBean(.class) 獲取 bean 某種條件下出錯而引出對 通過指定bean 類型獲取的源碼細讀--isTypeMatch 方法

先明確(暫不討論懶加載等特殊):

問題現象

在使用spring-aop的時候 (配置類上面 加 @EnableAspectJAutoProxy 代表支持aop xxx)

  1. 配置類appconfg 上面 使用默認的代理模式即 jdk動態代理的策略後,在使用 ioc.get(bean)的時候
    如果通過的 類型方式獲取bean 且 直接通過 子類的類型來獲取容器 bean的時候,就會報錯
  2. 而採用cglib即 在配置類上面開啓支持aop 功能同時 設置 proxyTargetClass = true 的方式
    同樣按照1.的方式去或取bean不會出錯–
    (因爲:即使bean是實現接口的bean,但是採用的代理方式cglib, 是直接對所代理對象即bean 基於它生產的子類 作爲代理的)
    (注: 測試類bean是實現接口的bean )

1.一般我們獲取bean的兩種方式: getBean(“xxbeanName”) 或者 getBean(xxBean.calss)

這兩種情況即 a. 一種通過beanName, b.一種是bean類型 ,
其再源碼內最終都是通過 先找到 beanName 後去找到對應的 bean 的

2.即 b.種通過類型來獲取其實內部是先做了轉換的, --> 最終他們都是通過 getBean(String name) 來xx)

.

3. getBean(xxBean.calss)這種獲取方式裏面核心就是看如何通過Type 轉換到 beanName

.
即主要方法 resolveNamedBean(requiredType, args); --> 它裏面主要 String[] candidateNames = getBeanNamesForType(requiredType); (返回數組是考慮到一個類型是如接口可能有對應多個bean)裏面如何實現,下面就是**聚焦這個轉換方法:**
(0.)先從 緩存中是否能直接取到 String[] resolvedBeanNames = cache.get(type); 此緩存是專門用於xx
Map<Class<?>, String[]> allBeanNamesByType
(1.)有直接返回name 了,沒取過是肯定拿不到,那麼走doGetBeanNamesForType:
先得到 所有beanDefinitionNames–>目標就是從這個集合中能夠用 此xxBean.class 匹配到一個所需的beanName,
(2) 循環 beanDefinitionNames 讓每個beanNam 都機會去匹配 所傳入的 type 的邏輯裏(它還兼容處理了 FactoryBean特殊) ,即每個beanName 都會執行下面:
–> 所定義關鍵的 boolean matchFound ,–> 裏面最!關鍵代碼 isTypeMatch (beanName, typeToMatch) -->
從此beanName又先 getSingleton (一般能得到的其 對應bean) --> 然後 typeToMatch.isInstance(beanInstance),(即判斷此beanName 所對應取得的bean 是否和 所匹配的 type 是同一類型內部:ClassUtils.isAssignable(clazz, other) – lhsType.isAssignableFrom(resolvedWrapper))
–>

如果類型同,即代表所要找的就是這個beanName,   matchFound= true; 
            並將此 beanName加入resolvedBeanNames 返回 
            注意: 這裏就是本場最重要的點了!! --》 有可能出錯的原因就是這個 始終再容器裏的所有的bean所對應的類型 和 你傳入的 bean的類型 沒有一個是相同的!! 即最終返回 空,後就會直接因找不到對應 beanName 拋異常!
(3.)最後再通過此返回找到的 beanName 再次調用 getBean(BeanName) 得到此 bean

而現在的討論的問題的引出就是這種b.通過類型 獲取bean 所引發問題的思考

4. 關鍵方法isTypeMatch (beanName, typeToMatch)的思考

isTypeMatch (beanName, typeToMatch) 這個方法其實就是最核心的一個完成上述功能的,但是它的所作用的地方卻不僅在此裏,還在其他地方,如解決spring
的循環依賴中發揮了關鍵作用。
具體是 spring將循環依賴的關係對象都確定完成後,對其第一個所要依賴的對象屬性並不是 馬上 feild.set(x,y) ,而是 需要經過一次 對得到的屬性對象 的一次類型檢查匹配 纔可以,即會調用到 isTypeMatch (beanName, typeToMatch) ,
這也是循環依賴爲啥需要做檢查類型的原因了!–> 因爲如果依賴的候選bean是被jdk動態代理過的化,就有可能出現類似我上面的問題!!
這裏面從那個 earlySingltonxxxMap 裏面取出返回的對象 和 本生的對象屬性類型
比對,一致後,才進行 feild.set() 操作。 — 具體的循環依賴等有空再寫一篇專門分析!!

------------- 下面開始問題現象就很好解釋了--------------------------------

在這裏插入圖片描述

二.而如果再appconfig 開啓代理時候 配置了 @EnableAspectJAutoProxy(proxyTargetClass = true)就不一樣了:

–>1. 因爲 這樣之後,就算是所代理的類是實現了接口的,那麼它還是會採用 cglib 這種代理策略;
cglib 他是基於 當前這個實現類 去生產出的代理對象是 這個實現類的子類,即所生產的 proxy 和原代理 是子父 關係,即後面你去用 get(這個實現類.class ) 的時候, 再後面執行所以beanName 的匹配階段,是可以
和 它對應的 代理類對象匹配到,(他們屬於同種類型!)
CGLIB是一個代碼生成類庫,可以在運行時候動態是生成某個類的子類
證明:在這裏插入圖片描述

2.不像jdk 代理, 它所生產的 代理對象 和 實現類 是平級 關係, 匹配不到, 因爲類型不一致,找不到他所想要找的 beanName.
在這裏插入圖片描述

注意:上述xxx, 其實都是 spring-aop的內容,雖然用到了 aspectj 的註解 配置等等,但是也相當於是隻是借用了 aspectj 的技術而已!!, 因爲,我們跟源碼或是其他,根本沒有看到有再編譯階段就 產出了 代理對象的!,即 沒有通過它所提供的 ajc 編譯過!!,這也是 spring-aop 和 aspectj 的區別和關係。
(只是說這樣讓spring 和 aspectj 產生了關係!!!,沒有用到aspectj 這種代理模式!)

在這裏插入圖片描述

參考:https://segmentfault.com/a/1190000020621578

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章