spring自動注入(手動裝配)

前言

文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵大家在技術的路上寫博客

絮叨

這篇文章 我是參考子路老師的,它講的Spring真心不錯,我寫文章就是寫把它的知識吸收變成自己的,因爲自己可能還是很菜,只能吸取別人的經驗了。

前言

比如提到spring的自動注入作爲一個java程序員肯定自信無比了解;但是筆者要說的自動注入可能會和你理解有很大出入。首先搞明白什麼是自動注入,自動注入也可以叫做自動裝配(springboot也有一個自動裝配但是我認爲翻譯的不夠準確,springboot的應該叫做自動配置和這裏說的自動注入是兩回事,筆者不是什麼大牛或者權威;所以讀者如果你堅持認爲springboot也叫自動裝配那也無可厚非,只是在這篇文章裏面所謂的自動注入就是自動裝配,關於springboot的自動配置我以後更新文章再來說);自動注入需要相對於手動裝配來說;在spring應用程序當中假設你的A類依賴了B類,需要在A類當中提供一個B類的屬性,再加上setter,繼而在xml當中配置、描述一下這兩個類之間的依賴關係。如果做完當容器初始化過程中會實例化A,在實例化A的過程中會填充屬性,由於在xml中已經配置、描述好兩者的關係,故而spring會把B給A裝配上;這種由程序員自己配置、描述好依賴關係的寫法叫做手動裝配;看個例子吧;

package com.luban.app;


public class A {
	B b;
	public void setB(B b) {
		this.b = b;
	}
}






package com.luban.app;


public class B {


}


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


		<bean id="a" class="com.luban.app.A" >
			<!-- 由程序員手動指定的依賴關係 稱爲手動裝配-->
			<property name="b">
				<ref bean="b" />
			</property>
		</bean>


		<bean id="b"  class="com.luban.app.B">
		</bean>
</beans>

上面的這種情況就是手動配置,通過xml 定義好2個類的屬性關係。

但是實際開發中手動裝配的場景比較少(比如在缺少源碼的情況下可能會使用這種手動裝配情況);關於依賴注入的資料可以參考官網Spring

這一章節提到了一個非常重要的知識點,也是一個常見的spring面試題目。spring有幾種依賴注入方式?那麼這個問題應該怎麼回答呢?

上面這個是我谷歌翻譯的Spring的文檔,英語比較菜沒辦法,那麼我們來回答一下上面的面試題吧

官網的意思是DI(依賴注入)一共有兩種主要的變體(注意會考),分別是基於構造方法的依賴注入和基於setter(setXxxx(…))的依賴注入,不管是手動裝配還是自動裝配都是基於這兩種方式或者變體方式來的;但是這裏一定要回答到主要和變體兩個名詞,因爲有的注入方式就不是這兩種,而是這兩種其中一種的變體方式;比如在一個類的屬性上面加@Autowired,這種方式注入屬性的方式就是利用了java的反射知識,field.set(value,targetObject);關於這個我在後面的文章中對spring源碼解析的時候會說明@Autowired的原理;所以@Autowired這種注入的方式是setter注入方式的一種變體 但是這裏需要說明的是所謂的setter其實和屬性無關,什麼意思呢?一般的setter方法會對應一個屬性,但是spring的基於setter的注入方式是不需要屬性的,僅僅只需要一個setter方法,下面這個例子來說明這個問題

B.java


public class B {


}




A.java


public class A {
	public void setXxx(B b) {
		System.out.println("spring 找到符合的setter");
		System.out.println("和屬性無關,甚至可以不要屬性");
		System.out.println("可以直接調用,這個A裏面就沒有任何屬性");
	}
}


xml配置文件


<bean id="a" class="com.luban.app.A" >
	<!-- 由程序員手動指定的依賴關係 稱爲手動裝配-->
	<property name="xxx">
		<ref bean="b" />
	</property>
</bean>


<bean id="b"  class="com.luban.app.B">
</bean>

如上面的結構 雖然A裏面注入了B,但是A裏面並沒有B的屬性

運行上面的代碼可以看到spring也會調用這個setXxx方法,如果仔細觀察調用棧可以看到這個方法是在spring容器初始化的時候實例化A,完成A的注入功能時候調用過來的,下圖是筆者運行結果的截圖

可能有的讀者會認爲上面的xml配置文件中已經手動裝配了B給A,肯定會調用setXxx方法,其實不然,即是我使用自動裝配也還是會調用的(關於什麼是自動裝配,下文會詳細介紹),因爲前文說過,注入方式與手動注入、自動注入無關。筆者改一下代碼,把A的注入模型改成自動注入來看看結果

<!-- 程序員不指定裝配的具體參數,容器自動查詢後裝配-->
<bean id="a" class="com.luban.app.A" autowire="byType">
	
</bean>


<bean id="b"  class="com.luban.app.B">
</bean>


把代碼改成上面這樣自動裝配結果是一樣的,A類當中的setXxx方法還是會調用,不需要提供任何屬性,至於原理筆者後面更新到spring源碼的時候再來詳細說道;可能有讀者會說如果使用註解呢?比如如下代碼

A.java


@Component
public class A {
	@Autowired
	B b;
	public void setB(B b) {
		System.out.println("如果你使用註解,這個setter變得毫無意義");
		System.out.println("這個方法甚至都不會調用");
		System.out.println("因爲前文說過這種方法用的是field.set");
	}
}


B.java


import org.springframework.stereotype.Component;
@Component
public class B {


}


上面代碼同樣會注入b,但是是調用field.set去完成注入的,不是通過setter,當然再次說明一下這是setter的一種變體;上面代碼直到A完成注入B後setter方法也不會調用;

重點來了,要考。筆者看過很多資料說@Autowired也算自動裝配,關於這點筆者不敢苟同,在一個屬性上面加@Autowired註解應該屬於手動裝配,我會通過大量源碼和例子來證明筆者的這個理論。之所以會有人把@Autowired也理解爲自動裝配的原因是因爲bytype引起的,因爲spring官網有說明自動裝配有四種模型分表是no、bytype、byname、constructor;參考官網資料,而現在流行着這麼一種說法:@Autowired就是通過bytype來完成注入的。如果你也認爲這種說法成立,那麼就是默認了@Autowired是自動裝配且裝配模型是bytype。筆者見過很多程序員和很多資料都支持這種說法,其實嚴格意義上來講這句話大錯特錯,甚至有誤人子弟的嫌疑。因爲bytype僅僅是一種自動注入模型而已,這種模型有其固有的技術手段,而@Autowired是一個註解,這個註解會被spring的後置處理器解析,和處理bytype不是同一回事。如果需要講清楚他們的區別和證明@Autowired不是自動裝配則首先要搞明白什麼自動裝配。筆者接下來會花一定篇幅來解釋自動裝配的知識,然後回過頭來講他們的區別和證明@Autowired屬性手動裝配

如果現在已經理解了手動裝配也叫手動注入,也已經理解了注入方式(setter和構造方法),那麼接下來討論自動注入或者叫自動裝配;自動注入的出現是因爲手動裝配過於麻煩,比如某個類X當中依賴了10個其他類那麼配置文件將會變的特別冗餘和臃腫,spring的做法是可以爲這個X類提供一種叫做自動裝配的模型,無需程序員去手動配置X類的依賴關係。有讀者會疑問,用註解不也是可以解決這個xml臃腫的問題?確實用註解可以解決,但是我們現在討論的是自動裝配的問題,就不能用註解;爲什麼不能用註解來討論自動裝配的問題呢?因爲在不配置BeanFactoryPostProcessor和修改beanDefinition的情況下註解的類是不支持自動裝配的(關於BeanFactoryPostProcessor和beanDefinition的知識筆者以後有時間更新);這也是證明@Autowired默認不是自動裝配的一個證據,那又如何證明註解類是默認不支持自動裝配呢?下文我會解釋一個註解類默認是不支持自動裝配的。也就是說如果討論自動裝配最好是用xml形式來配置spring容器纔會有意義;當然讀者如果對spring比較精通其實通過修改註解類的beanDefinition來說明自動裝配的問題,鑑於大部分讀者對spring不精通故而筆者還是用xml來討論吧;比如下面這個例子

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
		default-autowire="byType">
		<!--default-autowire="byType"-->
		<!-- 程序員不指定裝配的具體參數,容器自動查詢後裝配-->
		<!--當然除了這種寫法還可以直接在bean標籤上爲特定類指定自動裝配模型-->
		<bean id="a" class="com.luban.app.A">


		</bean>


		<bean id="b"  class="com.luban.app.B">
		</bean>
</beans>




A.java






public class A {
	B b;
	public void setB(B b) {
		System.out.println("在不考慮註解的情況下");
		System.out.println("如果配置了自動裝配則不需要手動配置");
	}
}


B.java


public class B {


}


上面代碼運行起來,A能注入B,但是在xml配置文件中並沒有去手動維護、描述他們之間的依賴關係,而是在xml的根標籤上面寫了一行default-autowire=“byType”,其實關於自動注入的歧義或者被人誤解的地方就是這個default-autowire="byType"引起的;那麼這行代碼表示什麼意思呢?表示所在配置在當前xml當中的bean都以bytype這種模式自動裝配(如果沒有特殊指定,因爲bean還可以單獨配置裝配模式的);這需要注意筆者的措辭,筆者說的bytype這自動裝配模式,是一種模式,這個不是筆者信口開河,因爲在官網文檔裏面spring也是這麼定義的

Autowiring mode 筆者姑且翻譯爲自動注入模型吧,如果有211、雅思讀者可以給讀者留言更合適的翻譯 那爲什麼要錙銖在這個名詞上呢?筆者覺得真的非常重要,很多初學spring的程序員都是粗學spring,忽略甚至混淆了很多關鍵的名詞定義,導致很多觀念根深蒂固後面想深入學習spring源碼的時候就比較困難 這個名字叫做自動注入模型和前面提到的依賴注入方式(setter和構造方法)是兩回事,簡而言之:依賴注入是一個過程,主要通過setter和構造方法以及一些變體的方式完成把對象依賴、或者填充上的這個過程叫做依賴注入,不管手動裝配還是自動裝配都有這個過程;而自動裝配模型是一種完成自動裝配依賴的手段體現,每一種模型都使用了不同的技術去查找和填充bean;而從spring官網上面可以看到spring只提出了4中自動裝配模型(嚴格意義上是三種、因爲第一種是no,表示不使用自動裝配、使用),這四個模型分別用一個整形來表示,存在spring的beanDefinition當中,任何一個類默認是no這個裝配模型,也就是一個被註解的類默認的裝配模型是no也就是手動裝配;其中no用0來表示;bytype用2來表示;如果某個類X,假設X的bean對應的beanDefinition當中的autowireMode=2則表示這個類X的自動裝配模型爲bytype;如果autowireMode=1則表示爲byname裝配模型;需要注意的是官網上面說的四種注入模型其中並沒有我們熟悉的@Autowired,這也再一次說明@Autowired不是自動裝配;可能有讀者會提出假設我在A類的某個屬性上面加上@Autowired之後這個A類就會不會成了自動裝配呢?@Autowired是不是會改變這個類A當中的autowireMode呢?我們可以寫一個例子來證明一下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
	   default-autowire="byType">


		<bean id="a" class="com.luban.app.A">
		</bean>
		<bean id="b"  class="com.luban.app.B">
		</bean>
</beans>


xml配置了A 和B 都是自動裝配模型爲bytype講道理要實現autowireMode=2


A.java
public class A {
	
}


B.java
public class B {
	
}






提供一個後置處理器來獲取A的自動裝配模型
ZiluBeanFactoryPostprocessor.java
@Component
public class ZiluBeanFactoryPostprocessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		GenericBeanDefinition a = (GenericBeanDefinition)
				beanFactory.getBeanDefinition("a");
		//打印A 的注入模型
		System.out.println("a mode="+a.getAutowireMode());


	}
}


講道理由於是bytype所以應該打印出來2;結果如圖

如果筆者把注入模型改成byname則結果應該會改變

接下來筆者驗證一個通過註解配置的類加上@Autowried後的注入模型的值

可以看到結果爲0,說明這個A類不是自動裝配,其實這已經能證明@Autowried不是自動裝配了,但是還有更直接的證據證明他不是自動裝配,就是通過spring源碼當中處理@Autowried的代碼可以看出,首先我們寫一個bytype的例子看看spring如何處理的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
	   default-autowire="byType">
		<!--自動裝配-->
		<bean id="a" class="com.luban.app.A">
		</bean>
		<bean id="b"  class="com.luban.app.B">
		</bean>
</beans>






A.java


public class A {


	B b;


	/**
	 * 如果是自動裝配需要提供setter
	 * 或者提供構造方法
	 */
	public void setB(B b) {
		this.b = b;
	}
}


B.java
public class B {


}


上面代碼運行起來,調試spring源碼當中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean方法,這個方法主要就是完成屬性填充的,也就是大家說的注入

可以看上圖1399行有一個判斷,判斷當前類的注入模式是否bynane或者bytype如果是其中一種則會進入1401行代碼執行自動裝配的邏輯;因爲當前代碼我在xml當中配置了自動注入模型爲bytype所以這裏一定會進入,從上圖debug的結果我們可以得知確實也進入了1401行

但是如果當我們使用@Autowired註解去注入一個屬性的時候spring在完成屬性注入的過程中和自動注入(byname、bytype)的過程不同,spring註解跳過了那個判斷,因爲不成立,而是用後面的代碼去完成屬性注入;這也是能說明@Autowired不是自動裝配的證據、更是直接打臉@Autowired是先bytype的這種說法

結論

以後如果再聽到@Autowried是bytype請你糾正他,bytype是一種自動注入模型;@Autowried是一個註解,兩個人沒有關係,一點關係都沒有;@Autowried講道理算是手動裝配 我覺得只能說@Autowried是自動注入,手動裝配,因爲它的裝配類型爲0

結尾

Spring的東西還是很多,大家慢慢學吧

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是真粉

創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見

六脈神劍 | 文 【原創】如果本篇博客有任何錯誤,請批評指教,不勝感激 !

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