《Spring3實戰》摘要(3-1)--最小化Spring XML配置之自動裝配Bean屬性

第三章 最小化Spring XML配置

隨着應用的不斷髮展,我們將不得不編寫越來越複雜的XML配置。幸運的是,Spring提供了幾種技巧,可以幫助我們減少XML的配置數量。

  • 自動裝配(autowiring):有助於減少甚至消除配置<property>元素和<constructor-arg>元素,讓Spring自動識別如何裝配Bean的依賴關係。
  • 自動檢測(autodiscovery):比自動裝配更進一步,讓Spring能夠自動識別哪些類需要被配置成Spring Bean,從而減少對<bean>元素的使用。

3.1 自動裝配Bean屬性

3.1.1 4種類型的自動裝配

當涉及自動裝配Bean的依賴關係時,Spring有多種處理方式。因此,Spring提供了4種各具特色的自動裝配策略。

  • byName——把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。如果沒有跟屬性的名字相匹配的Bean,則該屬性不進行裝配。

  • byType——-把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。如果沒有跟屬性的類型相匹配的Bean,則該屬性不被裝配。如果Spring尋找到多個符合要求的Bean,Spring會拋出異常。爲了避免因爲使用byType自動裝配而帶來的歧義,Spring爲我們提供了另外兩種選擇:可以爲自動裝配標識一個首選Bean,或者可以取消某個Bean自動裝配的候選資格。

    • 爲自動裝配標識一個首選Bean,可以使用<bean>元素的primary屬性。如果只有一個自動裝配的候選Bean的primary屬性設置爲true,name該Bean將比其他候選Bean優先被選擇。但是primary屬性默認設置爲true,所以爲了使用primary屬性,需要將所有非首選Bean的primary屬性設置爲false。
    • 取消某個Bean的候選資格,可以設置這些Bean的autowire-candidate屬性爲false。
  • constructor——把與Bean的構造器入參具有相同類型的其他Bean自動裝配到Bean構造器的對應入參中。constructor自動裝配具有和byType自動裝配相同的侷限性。當發現多個Bean匹配某個構造器入參時,Spring會報錯。如果一個類有多個構造器,它們都滿足自動裝配的條件時,spring也會報錯。

  • autodetect——-首先嚐試使用constructor進行自動裝配。如果失敗,再嘗試使用byType進行自動裝配。

<!-- 通過設置autowire屬性爲byName,Spring將爲kenny的所有屬性尋找與其名字相同的Spring Bean。在這裏,Spring會發現instrument屬性可以通過setter注入來進行自動裝配。 -->
<bean id="instrument" class="com.springinaction.springidol.Saxophone" />

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist" autowire="byName">
    <property name="song" value="Jingle Bells" />
</bean>

3.1.2 默認自動裝配

如果需要爲Spring應用上下文中的每一個Bean(或者其中的大多數)配置相同的autowire屬性。我們可以在根元素<bean>上增加一個default-autowire屬性。默認情況下,default-autowire屬性爲none。該屬性配置僅對該配置文件下的Bean有效,我們也可以使用<bean>元素的autowire屬性來覆蓋默認自動裝配策略。
這裏寫圖片描述

3.1.3 混合使用自動裝配和顯式裝配

我們對某個Bean選擇了自動裝配策略時,也可以同時對該Bean的某些屬性進行顯式裝配。我們仍然可以爲任意一個屬性配置<property>元素。
這裏寫圖片描述

3.2 使用註解裝配

從Spring 2.5 開始,Spring開始支持使用註解自動裝配Bean的屬性。使用註解自動裝配與在XML中使用autowire屬性自動裝配並沒有太大的差別。但是使用註解方式允許更細粒度的自動裝配。
Spring容器默認禁用註解裝配。所以,在使用基於註解的自動裝配前,我們需要在Spring配置中啓用它。最簡單的啓用方式是使用Spring的context命名空間配置中的<context:annotation-config />元素。

這裏寫圖片描述

Spring 3 支持幾種不同的用於自動裝配的註解:

  • Spring自帶的@Autowired註解;
  • JSR-330 (Java依賴注入標準)的 @Inject 註解;
  • JSR-250 的 @Resource 註解。

3.2.1 使用 @Autowired

Spring的@Autowired註解是減少Spring XML配置的一種方式。但使用它的類會引入對Spring的特定依賴。

(1)Autowired註解標註setter方法,Spring 會嘗試對該方法執行byType自動裝配。

@Autowired
public void setInstrument(Instrument instrument){
    this.instrument = instrument;
}

(2)Autowired註解可以標註需要自動裝配Bean引用的任意方法。

@Autowired
public void heresYourInstrument(Instrument instrument){
    this.instrument = instrument;
}

(3)Autowired註解標註構造器

@Autowired
public Instrumentalist(Instrument instrument){
    this.instrument = instrument;
}

(4)Autowired註解可直接標註屬性,不需要setter方法。

@Autowired
private Instrument instrument;

可選的自動裝配:配置required屬性爲false

默認情況下,@Autiwired具有強契約特徵,其所標註的屬性或參數必須是可以裝配的。如果沒有Bean可以裝配到@Autowire的所標註的屬性或參數中,自動裝配就會失敗(NoSuchBeanDefinitionWxception)。

通過設置@Autowired的required屬性爲false來配置自動裝配是可選的。當Spring沒有查找到與之匹配的類型爲Instrument的Bean,應用就不會發生任何問題,而裝配的屬性值會設置爲null。

注意,required屬性可以用於@Autowired註解所使用的任意地方。但是當使用構造器裝配時,只有一個構造器可以將@Autowired的requeired屬性設置爲true,其他使用@Autowired所標註的構造器只能將required屬性設置爲false。此外,當使用@Autowired標註多個構造器時,Spring就會從所有滿足裝配條件的構造器中選擇入參最多的那個構造器。

@Autowired(required=false)
private Instrument instrument;

限定歧義性的依賴:使用@Qualifier註解

對於具有兩個或多個bean都滿足裝配條件的情況,@Autowired註解沒有辦法選擇哪一個Bean纔是它真正需要的。所以會拋出NoUniqueBeanDefinitionException異常,明確表明裝配失敗了。

我們可以配合使用Spring的@Qualifier註解來幫助@Autowired鑑別出哪一個Bean纔是我們所需要的。

<!-- 使用@Qualifier來明確指定裝配id爲guitar的Bean -->
@Autowired
@Qualifier("guitar")
private Instrument instrument;

<!-- 通過在Bean上直接使用Qualifier來縮小範圍 -->
<bean class="com.springinaction.springidol.Guitar">
    <qualifier value="stringed" />
</bean>
<!-- 除了可以在 XML 中指定 qualifier,還可以使用 @Qualifier 註解來標註 Guitar 類-->
@Qualifier("stringed")
public class Guitar implements Instruments{
    ...
}

創建自定義的限定器(Qualifier):

/**
 * 定義一個註解,並使用@Qualifier註解作爲它的元註解。例如,讓我們創建自己的@StringedInstrument註解來充當限定器。
 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface StringedInstrument{
}

/**
 * 通過自定義註解來代替@Qualifier標註Guitar
 */
 @StringedInstrument
 public class Guitar implements Instrument{
 }

/**
 * 使用@StringedInstruement 對自動裝配的instrument屬性進行限定
 */
 @Autowired
 @StringedInstrument
 private Instrument instrument;

3.2.2 藉助@Inject實現基於標準的自動裝配

從 Spring 3 開始,Spring 開始兼容 JSR-330 依賴注入規範。

@Inject註解是 JSR-330 的核心部件。該註解幾乎可以完全替換 Spring 的 @Autowired 註解。使用 @Inject 註解,需要添加javax.inject包。

和 @Autowired 一樣,@Inject 可以用來自動裝配屬性、方法和構造器;與 @Autowired 不太的是,@Inject 沒有 required 屬性。因此,@Inject 註解所標註的依賴關係必須存在,如果不存在,則會拋出異常。

/**
 * 除了 @Inject 註解,JSR-330 規範還提供了另一種技巧。Provider 接口可以實現 Bean 引用的延遲注入以及注入 Bean 的多個實例等功能。
 * 我們有一個knifeJuggler類需要注入一個或多個knife的實例。假設 knife bean 的作用域聲明爲 prototype,下面的 KnifeJuggler 的構造器將獲得5個 Knife Bean:
 * KnifeJuggler 將獲得一個 Provider<Knife> ,而不是在構造器中獲得一個Knife實例。這個時候,只有 provider 被注入進去;在調用provider的 get() 方法前,實際的 Knife 對象並沒有被注入。
 */
 private Set<Knife> knives;
 @Inject
 public KnifeJuggler(Provider<Knife> knifeProvider){
    knives = new HashSet<Knife>();
    for(int i=0;i<5;i++){
        knives.add(knifeProvider.get());
    }
 }

限定 @Inject 所標註的屬性:@Named註解

@Inject 註解與 @Autowired 註解一樣易導致歧義性的Bean定義。相對於 @Autowired 所對應的 @Qualifier,@Inject 所對應的是 @Named 註解。

import javax.inject.Inject;
import javax.inject.Named;
...

@Inject
@Named("guitar")
private Instrument instrument;

創建自定義的 JSR-330 Qualifier

JSR-330 在 javax.inject 包裏有自己的 @Qualifier 註解。JSR-330 不建議使用該註解,但 JSR-330 鼓勵我們使用該註解來創建自定義的限定器註解,就像我們使用Spring 的 @Qualifier 來創建自定義註解一樣。實際上,@Named 註解就是一個使用 @Qualifier 註解所標註的註解。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Documented;
import javax.inject.Qualifier;

@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@Documented
public @interface StringedInstrument{
}

3.2.3 在註解注入中使用表達式:@Value

Spring 3.0 引入了 @Value ,它是一個新的裝配註解,可以讓我們使用註解裝配String類型的值和基本類型的值。

我們可以通過 @Value 直接標註某個屬性、方法或者方法參數,並傳入一個String類型的表達式來裝配屬性,例如:

@Value("Eruption")
private String song;

裝配簡單的值並不是 @Value 所擅長的,不過,可以藉助 SpEL 表達式,是使@Value 的功能得到強化。它是一種有效的基於註解驅動的裝配方式,它可以根據SpEL表達式進行動態的求值計算。例如:

@Value("#{systemProperties.myFavoriteSong}")
private String song;
發佈了66 篇原創文章 · 獲贊 37 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章