@Autowired淺析
首先放幾段代碼和幾個問題,帶着思考去看接下來的結論
第一個問題
第一段代碼:容器初始化完畢,控制檯是否會打印(“This is a method from TeachService”)這句話?
@Service
public class StudyService {
public void setTeacherService(TeachService teacherService){
teacherService.teach();
}
}
@Service
public class TeachService {
public void teach(){
System.out.println("This is a method from TeachService");
}
}
答案是會,但是這裏我是做了一個細節上的配置,正常情況下,即默認情況是不會打印的,稍後細說。
第二個問題
說起@Autowired註解,大家想到的就是byType,那麼看下面的代碼。
//聲明一個接口
public interface People {
void showIdentity();
}
//寫兩個實現類,都注入到spring容器中
@Service
public class StudyService implements People {
@Override
public void showIdentity() {
System.out.println("I am a student");
}
}
@Service
public class TeachService implements People {
@Override
public void showIdentity() {
System.out.println("I am a teacher");
}
}
//使用另外的bean注入People類型,請注意,這裏注入的類型是接口,變量名使用的是studyService
//如果按照byType的理解,容器初始化時應該會報錯,同一個type找到多個bean,不知道注入哪個
@Service
public class Leader {
@Autowired
People studyService;
public void showYourIdentity(){
studyService.showIdentity();
}
}
其實呢,看下圖運行成果
第三個問題
讀過源碼的同學可能瞭解過populateBean方法裏有一段判斷邏輯
這裏的邏輯是判斷bean的注入類型是否爲byName或者byType,看到圖中的值,相信大家都知道,是不會進這個if分支的,那麼是爲什麼呢?接下來放一段官網對於注入的一個解釋。
下面是對於上面的一個部分翻譯
模式 | 解釋 |
---|---|
no | 非自動裝配,bean的應用需要通過ref元素定義 |
byName | 按屬性名稱自動裝配。Spring容器會尋找與需要自動實現的屬性同名的bean。 |
byType | 如果容器中恰好存在該屬性類型的一個bean,則允許該屬性被自動調用。如果存在多個,就會拋出一個異常,這表明不能對該bean使用byType自動裝配。 |
constructor | 類似於byType,但適用於構造函數參數。 |
問題解答
按照上述官網解釋,如果@Autowired註解真的是像大部分網上所說的byType,那麼我第二個問題的代碼怎麼可能運行成功呢?接下來用spring的後置處理器去獲取一下StudyService的BeanDefinition對象,通過BeanDefinition對象查看裝配類型究竟是什麼吧!
@Configuration
public class MyPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition s = (GenericBeanDefinition) beanFactory.getBeanDefinition("studyService");
System.out.println("默認裝配類型:"+s.getAutowireMode());
}
}
由上圖可知@Autowired的裝配類型爲0,即官網所說的NO-非自動裝配。
有的人可能疑問了,爲什麼叫非自動裝配?spring明明幫我們把需要的類創建出來了呀,這個問題接下來會解釋。
先回顧第一個問題,爲什麼明明沒有使用註解和xml,set方法中的bean依舊注入成功了呢?依舊用到了後置處理器。上代碼
@Configuration
public class MyPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition s = (GenericBeanDefinition) beanFactory.getBeanDefinition("studyService");
//重點在這裏,通過後置處理器得到BeanDefinition對象後,修改autowireMode的值爲2,即byType
s.setAutowireMode(2);
}
}
怎麼做到的已經展示完了,接下來結合這一和二的內容,給大家做一個簡短的解釋。
首先大家所理解的自動裝配,是以開發人員的角度來說:我是一個開發,spring幫我把TeachService創建並注入到了StudyService中,這個過程中,“我”並沒有參與什麼,只是聲明瞭一個註解。
這就好比:
以前我們大家喫飯,需要自己買菜->做飯->喫飯->洗碗,做完整個週期性的任務纔算完成;
現在有了餐館(spring),我們只需要到餐館(spring)聲明我要喫魚香肉絲,在(買菜->做飯->喫飯->洗 碗)這個週期中,我們只需做到“喫飯”即可。
請注意,上述描述的角度是我->餐館的顧客|開發人員->spring的使用者
不知道大家能不能明白我所說的。
接下來的是我個人理解了,可能有所錯誤,大家可以共同探討!!!
我理解的自動裝配是相對於spring角度,也就是餐館角度;
當我走進餐館時(我使用setTeachService方法),老闆看到我就“覺得”我想要魚香肉絲(對應autowiredMode修改爲2之後,spring容器發現setTeachService方法時),並沒有問我是否真的需要(比如我就是想單純實現一個setTeachService方法自己用呢,對吧),就直接給我上了一盤魚香肉絲飯。
簡單的來說:在autowiredMode不等於0時,當開發人員提供了一個spring認可的注入方式,即set和construct形式時,spring會直接注入這兩種DI形式中的bean(前提是bean)
這裏插一個小知識點
官方文檔說明了,DI(依賴注入)有兩種主要的變體,即基於構造器的依賴注入和基於setter的依賴注入。
即使是常用的@Autowired註解,也是通過反射生成set方法進行的賦值。
本菜鳥也是剛入行三年,如果上述有什麼問題,大家可以留言,我會積極改正和學習的。