做網(wǎng)站解析要多久百度應(yīng)用商店app下載安裝
一、 前言
平常我們在使用spring框架開發(fā)項目過程中,會使用@Autowired
注解進行屬性依賴注入,一般我們都是聲明接口類型來接收接口實現(xiàn)變量,那么使用父類類型接收子類變量,可以注入成功嗎?答案是肯定可以的!
二、結(jié)果驗證
我們在項目中聲明如下三個類:
1. 測試代碼
- TestParent
public class TestParent {protected void test() {System.out.println("I am TestParent...");}
}
- TestSon
importorg.springframework.stereotype.Component;@Component
public class TestSon extends TestParent {publicvoidtest() {System.out.println("I am TestSon...");}
}
- TestType
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;@Component
public class TestType {@Autowiredprivate TestParent testParent;@PostConstructpublicvoidinit() {System.out.println("=========================");testParent.test();System.out.println("=========================");}
}
2. 驗證測試
啟動項目:
可以看到注入成功了,說明依賴注入使用父類類型接收子類變量是沒有問題的。
3. @Autowired注解使用細節(jié)
還是上面的案例,我們修改一下TestParent類的代碼,把TestParent也交由Spring容器管理:
importorg.springframework.stereotype.Component;@Component
public class TestParent {protected void test() {System.out.println("I am TestParent...");}
}
運行測試:
可以發(fā)現(xiàn),此時也可以注入成功,但是執(zhí)行對象變成了父類,有經(jīng)驗的大佬已經(jīng)猜到是什么情況了,沒猜到的也沒有關(guān)系,我們再修改一下TestType類的代碼:
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importjavax.annotation.PostConstruct;@Component
public class TestType {@Autowiredprivate TestParent test;@PostConstructpublic void init() {System.out.println("=========================");test.test();System.out.println("=========================");}
運行結(jié)果:
此時居然注入報錯了,提示我們有兩個bean沖突了,不能進行依賴注入!
三、原理分析
為什么會出現(xiàn)上面的現(xiàn)象呢,是由于@Autowired
注入時,是先按照類型找到bean
實例名稱,再按照beanName
去獲取真正需要注入的bean
,如果有多個實例時,會嘗試通過需要注入的字段名稱與按照類型篩選出來的beanName
對比,如果能夠?qū)Ρ瘸鑫ㄒ?code>beanName,也會按照此beanName
去獲取bean
實例注入,如果不能夠確定唯一bean
實例,就會拋出異常了。
下面我們進行源碼跟蹤:
@Autowired
注解解析的核心邏輯入口在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
后置處理器中,我們進入該類中進行斷點調(diào)試。
通過閱讀源碼我們可以發(fā)現(xiàn),依賴注入入口方法是postProcessProperties()
方法:
進入org.springframework.beans.factory.annotation.InjectionMetadata#inject
方法:
繼續(xù)調(diào)試,由于我們目前是字段方式注入,所以選擇org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement
類:
查看方法細節(jié):
我們繼續(xù)跟蹤org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
方法:
進入org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
方法:
繼續(xù)進入org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
方法:
繼續(xù)進入org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
方法:
進入org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class, boolean, boolean)
看一下bean
的篩選邏輯:
跟蹤進入org.springframework.beans.factory.support.DefaultListableBeanFactory#doGetBeanNamesForType
方法:
查看類型匹配判斷方法org.springframework.beans.factory.support.AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType, boolean)
:
判斷核心方法org.springframework.core.ResolvableType#isInstance
:
看到isAssignableFrom
方法就知道為什么子類變量也可以成功注入父類類型了,此時子類變量也是可以成功匹配上的。
篩選出所有類型匹配的beanName
以后,回到org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
做一下是否可以進行依賴注入的判斷,返回beanName
信息:
然后回到org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
方法中,如果有多個類型,會按照字段名稱和beanName
匹配再篩選:
最終確定獲取到可以注入的bean
實例。
四、寫在最后
以上流程還是比較清晰的,分析過程中有一些分支流程沒有過度關(guān)注,有興趣的小伙伴也可以參考流程,自己進行debug調(diào)試分析。