天天看點

Spring 配置使用 - Bean 自動裝配

基本概念

自動裝配(autowire),意味着整個裝配過程是自動完成,而不再由我們手動去指定。

在 Spring 中自動裝配共有以下幾種方式:

名稱 說明
no 表示不使用自動裝配,此時必須手動指定依賴的 Bean
byName 根據屬性名自動裝配,預設會檢查整個容器中與屬性名稱相同的 Bean 辨別。
byType 根據屬性的類型自動裝配,預設會檢查整個容器與屬性類型相同的 Bean。
constructor 與 byType 相似,同樣是根據類型自動裝配,不同的是這裡比對的是構造參數的類型,主要用于 constructor(構造) 注入
default 與 beans 标簽的 default-autowire 屬性有關
autodetect 判斷 Bean 的構造方法是否存在構造參數,若有則采用 constructor 自動裝配,否則采用 byType 自動裝配。目前已過時,Spring 3.0 以後不再被使用

no

意味着不使用自動裝配,采用手動配置的方式。這裡以構造注入為例,介紹下手動裝配的過程:

  • 首先定義用的 Bean
public class Cat{}

public class Animals {
    public Animals(Cat cat){ }
}
           
  • 在 xml 配置檔案中定義
<bean id="cat" class="com.demo.Cat" />

<!-- ①手動裝配,必須手動指定成員變量的依賴 Bean  -->
<bean id="animals" class="com.demo.Animals" >
    <constructor-arg name="cat" ref="cat"/>
</bean>

<!-- ②同樣意味這此時 autowire 屬性為 no ,同 ① 作用一樣 -->
<bean id="animals" class="com.demo.Animals" autowire="no">
    <constructor-arg name="cat" ref="cat"/>
</bean>
           
  • 建立容器驗證
public static void main(String [ ] args) {
    String location = ...
    ApplicationContext factory = new FileSystemXmlApplicationContext(location);
}
           

byName

根據屬性名自動裝配,預設會檢查整個容器中與屬性名稱相同的 Bean 辨別(包括 id,name,alias),并且該方式隻适用于 setter 注入。

  • 定義 Bean
public class Cat{}

public class Animals {
    private Cat cat;
    //省略 sertter/getter...
}
           
  • 在 xml 配置檔案中定義
<bean id="cat" class="com.demo.Cat" />

<!-- 與手動裝配相比,減少了配置的工作量 -->
<bean id="animals" class="com.demo.Animals" autowire="byName"/>
           
  • 調用驗證
String location = ...
ApplicationContext factory = new FileSystemXmlApplicationContext(location);
Animals animals = (Animals) factory.getBean("animals");
System.out.println(animals.getCat());
           

需要注意的是,當容器中不存在辨別與屬性名比對的 bean 時,容器預設不會抛出異常,而是将屬性當作 null 對待,即在調用 get 方法時預設輸出 null。該特性同樣适用于所有的 setter 注入。

byType

根據屬性的類型自動裝配,預設會檢查整個容器與屬性類型相同的 Bean,該方式隻适用于 setter 注入。

同 byName 的自動裝配方式相似,該方式的配置僅僅是将 byName 替換成 byType 而已,如下所示:

但是該方式存在着一個巨大的隐患,我們知道 byName 是通過在容器中查找與屬性名稱相同的 Bean,由于 Spring 容器的限制,是不允許存在兩個辨別相同的 bean,是以通過該方式比對的結果預設隻有一個比對的 Bean。

但是 byType 不一樣,Spring 容器并沒有限制相同類型 Bean 的存在,隻要它們的辨別不一樣,即是合理的。下面來探究下這種情況:

  • 定義 Bean
public class Cat{}

public class Animals {
    private Cat cat;
    //省略 sertter/getter...
}
           
  • 在 xml 配置檔案中定義
<!-- ①存在多個類型相同,辨別不同的 Bean -->
<bean id="cat1" class="com.demo.Cat" />
<bean id="cat2" class="com.demo.Cat" />

<!-- ②即便不指定辨別,Spring 也自動生成不同的辨別,作用同 ① -->
<bean  class="com.demo.Cat" />
<bean  class="com.demo.Cat" />

<bean id="animals" class="com.demo.Animals" autowire="byType"/>
           
  • 建立容器驗證

此時會抛出如下異常資訊:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type...
           

這是不是意味着在使用 byType 自動裝配時,容器中就不能存在相同類型的 Bean 呢,答案當然是否定的,Spring 通過引入競選機制來解決這一問題。

1.primary

當一個 Bean 将 primary 辨別為 true,就表示自己是主要的的 Bean,該值預設為 flase。當存在多個相同類型的 Bean 時,自動裝配時會優先選擇它。

<!-- 正确 -->
<bean  class="com.demo.Cat" primary="true" />
<bean  class="com.demo.Cat" />

<!-- 錯誤 -->
<bean  class="com.demo.Cat" primary="false" />
<bean  class="com.demo.Cat" />

<!-- 錯誤 -->
<bean  class="com.demo.Cat" primary="true" />
<bean  class="com.demo.Cat" primary="true" />
           

2.autowire-candidate

與 primary 意思相同,當一個 Bean 将 autowire-candidate 辨別為 false,就表示自己不再是候選的的 Bean,而是主要的 Bean ,該值預設為 true,defalut。當存在多個相同類型的 Bean 時,自動裝配時會優先選擇該屬性為 false 的 Bean。

<!-- 正确 -->
<bean  class="com.demo.Cat" autowire-candidate="false" />
<bean  class="com.demo.Cat" />

<!-- 錯誤 -->
<bean  class="com.demo.Cat" autowire-candidate="true" />
<bean  class="com.demo.Cat" />

<!-- 錯誤 -->
<bean  class="com.demo.Cat" autowire-candidate="default" />
<bean  class="com.demo.Cat" />

<!-- 錯誤 -->
<bean  class="com.demo.Cat" autowire-candidate="ture" />
<bean  class="com.demo.Cat" autowire-candidate="default"/>
           

constructor

與 byType 相似,同樣是根據類型自動裝配,不同的是這裡比對的是構造參數的類型,主要用于 constructor(構造) 注入。

  • 定義 Bean
public class Cat{}

public class Animals {
    public Animals(Cat cat){ }
}
           
  • 在 xml 配置檔案中定義
<bean  class="com.controller.Cat"  />

<bean id="animals" class="com.demo.Animals" autowire="constructor"/>
           

同樣該方式也存在相同類型 Bean 的隐患,解決方法與 byType 一緻,通過 primary、autowire-candidate 。

需要注意的是,與 byName 、byType 不同的是,該方式采用的構造注入,而不是 setter 注入。若 Bean 中沒有定義無參構造函數時,若在自動裝配時找不到比對的 Bean,則會抛出異常。而不會像 byName 、byType 把它當作 null 處理。

default

與 bean 的父标簽 beans 的 default-autowire 屬性有關,同 autowire 一緻,該屬性也有 no、default、byType、byName、constructor 這幾個值。

假設 beans 的 default-autowire 為 byType,則表示 bean 标簽中 autowire 為 default 都預設采用 byType 的注入方式。

具體配置形式如下:

<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-3.1.xsd" 
    default-autowire="byType">

    <bean  class="com.controller.Cat"  />

    <!-- 此時采用 byType 的自動裝配方式 -->
    <bean id="animals" class="com.demo.Animals" autowire="default"/>
</beans> 
           

autodetect

判斷 Bean 的構造方法是否存在構造參數,若有則采用 constructor 自動裝配,否則采用 byType 自動裝配。

Spring 3.0 以後該方式已被抛棄,這裡就不再探究。若想要配置該注入方式,需要要把3.0的 xsd 替換為 2.5 的 xsd,否則會報錯。