上篇文章咱們已經學習了
1.4
小結中關於依賴注入跟方法注入的內容。這篇文章咱們繼續學習這結中的其餘內容,順便解決下咱們上篇文章留下來的一個問題-----注入模型。web
前言:
在看下面的內容以前,咱們先要對自動注入及精確注入有一個大概的瞭解,所謂精確注入就是指,咱們經過構造函數或者setter方法指定了咱們對象之間的依賴,也就是咱們上篇文章中講到的依賴注入,而後Spring根據咱們指定的依賴關係,精確的給咱們完成了注入。那麼自動注入是什麼?咱們看下面一段代碼: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/dbeans/spring-beans.xsd"
>
<bean id="auto" class="com.dmz.official.service.AutoService" autowire="byType"/>
<bean id="dmzService" class="com.dmz.official.service.DmzService"/>
</beans>
public class AutoService {
DmzService service;
public void setService(DmzService dmzService){
System.out.println("注入dmzService"+dmzService);
service = dmzService;
}
}
public class DmzService {
}
public class Main03 {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc =
new ClassPathXmlApplicationContext("application.xml");
System.out.println(cc.getBean("auto"));
}
}
在上面的例子中咱們能夠看到數組
-
咱們沒有采用註解 @Autowired
進行注入 -
XML中沒有指定屬性標籤 <property>
-
沒有使用構造函數
可是,打印結果以下:微信
注入dmzServicecom.dmz.official.service.DmzService@73a8dfcc // 這裏完成了注入
com.dmz.official.service.AutoService@1963006a
可能細心的同窗已經發現了,在AutoService
的標籤中咱們新增了一個屬性autowire="byType"
,那麼這個屬性是什麼意思呢?爲何加這個屬性就能幫咱們完成注入呢?不要急,咱們帶着問題繼續往下看。app
自動注入:
這部份內容主要涉及官網中的1.4.5小結。編輯器
咱們先看官網上怎麼說的:函數

自動注入的優勢:
大概翻譯以下:工具
Spring能夠自動注入互相協做的bean之間的依賴。自動注入有如下兩個好處:學習
-
自動注入能顯著的減小咱們指定屬性或構造參數的必要。這個不難理解,咱們在上篇文章中講過了,依賴注入的兩種方式,setter方法跟構造函數,見上篇文章 依賴注入。在前言中的例子咱們也能發現,咱們並不須要指定屬性或構造參數 自動裝配能夠隨着對象的演化更新配置。例如,若是須要向類添加依賴項,則能夠自動知足該依賴項,而不須要修改配置。所以,自動裝配在開發過程中特別有用,可是當咱們的代碼庫變的穩定時,自動裝配也不會影響咱們將裝配方式切換到精確注入(這個詞是我根據官網閱讀加本身理解翻譯過來的,也就是官網中的(explicit wiring)
注入模型:
接下來,官網給咱們介紹了自動注入的四種模型,如圖:

咱們一一進行解析並測試:
-
no
這是目前Spring默認的注入模型,也能夠說默認狀況下Spring是關閉自動注入,必需要咱們經過setter方法或者構造函數完成依賴注入,而且Spring也不推薦修改默認配置。咱們使用IDEA時也能夠看到
用紅線框出來的部分建議咱們使用精確的方式注入依賴。
從上面來講,Spring自動注入這種方式在咱們實際開發中基本上用不到,可是爲了更好的理解跟學習Spring源碼,咱們也是須要好好學習這部分知識的。
-
byName
這種方式,咱們爲了讓Spring完成自動注入須要提供兩個條件
-
提供setter方法 -
若是須要注入的屬性爲 xxx
,那麼setter方法命名必須是setXxx
,也就是說,命名必須規範
在找不到對應名稱的bean的狀況下,Spring也不會報錯,只是不會給咱們完成注入。
測試代碼:
//記得須要將配置信息修改成:<bean id="auto" class="com.dmz.official.service.AutoService" autowire="byName"/>
public class AutoService {
DmzService dmzService;
/**
* setXXX,Spring會根據XXX到容器中找對應名稱的bean,找到了就完成注入
*/
public void setDmzService(DmzService dmzService){
System.out.println("注入dmzService"+dmzService);
service = dmzService;
}
}
另外我在測試的時候發現,這種狀況下,若是咱們提供的參數不規範也不會完成注入的,以下:
public class AutoService {
DmzService dmzService;
// indexService也被Spring所管理
IndexService indexService;
/**
* setXXX,Spring會根據XXX到容器中找對應名稱的bean,找到了就完成注入
*/
public void setDmzService(DmzService dmzService,IndexService indexService) {
System.out.println("注入dmzService" + dmzService);
this.dmzService = dmzService;
}
}
本覺得這種狀況Spring會注入dmzService,indexService爲null,實際測試過程當中發現這個set方法根本不會被調用,說明Spring在選擇方法時,還對參數進行了校驗,byName
這種注入模型下,參數只能是咱們待注入的類型且只能有一個
-
byType
測試代碼跟以前惟一不一樣的就是修改配置autowire="byType"
,這裏咱們測試如下三種異常狀況
-
找不到合適類型的bean,發現不報異常,同時不進行注入 -
找到了多個合適類型的bean,Spring會直接報錯 Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dmz.official.service.DmzService' available: expected single matching bean but found 2: dmzService,dmzService2
-
set方法中有兩個參數,切兩個參數都能找到惟一一個類型符合的bean,不報異常,也不進行注入
另外須要說明的是,我在測試的過程,將set方法僅僅命名爲set
,像這樣public void set(DmzService dmzService)
,這種狀況下Spring也不會進行注入
咱們能夠發現,對於這兩種注入模型都是依賴setter方法完成注入的,而且對setter方法命名有必定要求(只要咱們日常聽從代碼書寫規範,通常也不會踩到這些坑)。第一,不能有多個參數;第二,不能僅僅命名爲set
-
constructor
當咱們使用這種注入模型時,Spring會根據構造函數查找有沒有對應參數名稱的bean,有的話完成注入(跟前文的byName
差很少),若是根據名稱沒找到,那麼它會再根據類型進行查找,若是根據類型仍是沒找到,就會報錯。
自動注入的缺陷:
這裏不得不說一句,Spring官網在這一章節有三分之二的內容是在說自定注入的缺陷以及如何將一個類從自動注入中排除,結合默認狀況下自動注入是關閉的(默認注入模型爲no
),能夠說明,在實際使用狀況中,Spring是很是不推薦咱們開啓自動注入這種模型的。從官網中咱們總結自動注入有如下幾個缺陷:
-
精確注入會覆蓋自動注入。而且咱們不能注入基本數據類型,字符串,Class類型(這些數據的數組也不行)。並且這是Spring故意這樣設計的 -
自動注入不如精確注入準確。並且咱們在使用自動注入時,對象之間的依賴關係不明確 -
對於一些爲Spring容器生成文檔的工具,沒法獲取依賴關係 -
容器中的多個bean定義可能會與自動注入的setter方法或構造函數參數指定的類型匹配。對於數組、集合或映射實例,這可能不會產生什麼問題。可是,對於指望單個值的依賴項,咱們沒法隨意肯定到底有誰進行注入。若是沒有惟一的bean定義可用,則會拋出異常
如何將Bean從自動注入中排除?
這裏主要用到autowire-candidate
這個屬性,咱們要將其設置爲false
,這裏須要注意如下幾點:
-
這個設置只對類型注入生效。這也很好理解,例如咱們告訴Spring要自動注入一個 indexService
,同時咱們又在indexService
的配置中將其從自動注入中排除,這就是自相矛盾的。因此在byName
的注入模型下,Spring直接忽略了autowire-candidate
這個屬性 -
autowire-candidate=false
這個屬性表明的是,這個bean不做爲候選bean注入到別的bean中,而不是說這個bean不能接受別的bean的注入。例如在咱們上面的例子中咱們對AutoService
進行了以下配置:
<bean id="auto" class="com.dmz.official.service.AutoService" autowire="byType" autowire-candidate="false"/>
表明的是這個bean不會被注入到別的bean中,可是dmzService
任何會被注入到AutoService
中
另外須要說明的是,對於自動注入,通常咱們直接在頂級的
<?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="byName">
自動注入跟精確注入的比較總結:
連同上篇文章依賴注入,我畫了下面一個圖:

-
從關注的點上來看, 自動注入是針對的整個對象,或者一整批對象。好比咱們若是將 autoService
這個bean的注入模型設置爲byName
,Spring會爲咱們去尋找全部符合要求的名字(經過set方法)bean並注入到autoService
中。而 精確注入這種方式,是咱們針對對象中的某個屬性,好比咱們在autoService
中的dmzService
這個屬性字段上添加了@AutoWired
註解,表明咱們要精確的注入dmzService
這個屬性。而 方法注入主要是基於方法對對象進行注入。 -
咱們一般所說 byName, byType跟咱們在前文提到的注入模型中的 byName
,byType
是徹底不同的。一般咱們說的byName, byType是Spring尋找bean的手段。好比,當咱們注入模型爲constructor
時,Spring會先經過名稱找對符合要求的bean,這種經過名稱尋找對應的bean的方式咱們能夠稱爲byName
。咱們能夠將一次注入分爲兩個階段,首先是尋找符合要求的bean,其次再是將符合要求的bean注入。也能夠畫圖以下:

補充(1.4小結的剩餘部分)
這部分比較簡單,也是1.4
小節中剩餘的兩個小知識,在這篇文章咱們也一併學習了~
depends-on:
咱們首先要知道,默認狀況下,Spring在實例化容器中的對象時是按名稱進行天然排序進行實例化的。好比咱們如今有A,B,C三個對象,那麼Spring在實例化時會按照A,B,C這樣的順序進行實例化。可是在某些狀況下咱們可能須要讓B在A以前完成實例化,這個時候咱們就須要使用depends-on
這個屬性了。咱們能夠經過形以下面的配置完成:
<bean id="a" class="xx.xx.A" depends-on="b"/>
<bean id="b" class="xx.xx.B" />
或者:
@Component
@DependsOn("b")
public class A {
}
lazy:
默認狀況下,Spring會在容器啓動階段完成全部bean的實例化,以及一系列的生命週期回調。某些狀況下,咱們
可能須要讓某一個bean延遲實例化。這種狀況下,咱們須要用到lazy
屬性,有如下兩種方式:
-
XML中bean標籤的 lazy-init
屬性
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
-
@Lazy
註解
@Component
// 懶加載
@Lazy
public class A {
}
到此爲止,官網中1.4
小節中的內容咱們就全學習完啦!最核心的部分應該就是上文中的這個圖了。咱們主要總結了Spring讓對象產生依賴的方式,同時對各個方式進行了對比。經過這部分的學習,我以爲你們應該對Spring的依賴相關知識會更加系統,這樣咱們以後學習源碼時碰到疑惑也會少不少。
下面咱們還要繼續學習Spring的官網,好比前面文章提到的Beandefinition
究竟是什麼東西?Spring中的Bean的生命週期回調又是什麼?這些在官網中都能找到答案。
給本身加油,也給全部看到這篇文章的同窗加油~!!

本文分享自微信公衆號 - 程序員DMZ(programerDmz)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。