1. 程式人生 > >Spring學習(4) 最小化Spring XML配置

Spring學習(4) 最小化Spring XML配置

Spring提供了幾種技巧,可以幫助我們減少XML的配置數量。

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

當自動裝配和自動檢測一起使用時,它們可以顯著減少Spring的XML配置數量。通常只需要配置少量的幾行XML程式碼,而無需知道在Spring的應用上下文中究竟有多少Bean。

自動裝配Bean屬性

自動裝配(autowiring)

4種類型的自動裝配

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

  • byName——把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。如果沒有跟屬性的名字相匹配的Bean,則該屬性不進行裝配。
  • byType——把與Bean的屬性具有相同型別的其他Bean自動裝配到Bean的對應屬性中。如果沒有跟屬性的型別相匹配的Bean,則該屬性不被裝配。
  • constructor——把與Bean的構造器入參具有相同型別的其他Bean自動裝配到Bean構造器的對應入參中。
  • autodetect——首先嚐試使用constructor進行自動裝配。如果失敗,再嘗試使用byType進行自動裝配。

byName自動裝配

舉個栗子:

<bean
id="kenny2" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" /> <property name="instrument" ref="saxophone" /> </bean>

在這裡,我們使用元素顯式裝配了Kenny的instrument屬性。假設使用元素在定義薩克斯(saxophone)時,把Bean的id屬性設定為instrument:

<bean id="instrument"
class="com.springinaction.springIdol.Saxphone" />

在本示例中,薩克斯(saxophone)Bean的id屬性與Keeny Bean的instrument屬性的名字是一樣的。通過配置autowire屬性,Spring就可以利用此資訊自動裝配kenny的instrument屬性

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

byName自動裝配遵循一項約定:為屬性自動裝配ID與該屬性的名字相同的Bena。通過設定autowire屬性為byName,Spring將特殊對待kenny的所有屬性,為這些屬性尋找與其名字相同的Spring Bean。在這裡,Spring會發現instrument屬性可以通過setter注入來進行自動裝配。

使用byName自動裝配的缺點是需要假設Bean的名字與其他Bean的屬性的名字一樣。

byType自動裝配

byType自動裝配的工作方式類似於byName自動裝配,只不過不再是匹配屬性的名字而是檢查屬性的型別。當我們嘗試使用byType自動裝配時,Spring會尋找哪一個Bean的型別與屬性的型別相匹配。

constructor自動裝配

如果要通過構造器注入來配置Bean,那我們可以移除元素,由Spring在應用上下文中自動選擇Bean注入到構造器入參中。

例如:

<bean id="duke"
      class="com.springinaction.springidol.PoeticJuggler"
      autowire="constructor"
      />

上述宣告告訴Spring去審視PoeticJuggler的構造器,並嘗試在Spring配置中尋找匹配PoeticJuggler某一個構造器所有入參的Bean。

最佳自動裝配

如果想自動裝配Bean,但是又不能決定該使用那一種型別的自動裝配。現在不必擔心了,我們可以設定autuwire屬性為autodetect,由Spring來決定。例如

<bean id="duke"
      class="com.springinaction.springidol.PoeticJuggle"
      autowire="autodetect"/>

當配置一個Bean的autowire屬性為autodetect時,Spring將首先嚐試使用constructor自動裝配,如果沒有發現與構造器相匹配的Bean時,Spring將嘗試使用byType自動裝配。

預設自動裝配

default-autowire

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

使用註解裝配

從Spring2.5開始,最有趣的一種裝配Spring Bean的方式是使用註解自動裝配Bean的屬性。使用註解自動裝配與在XML中使用autowire屬性自動裝配並沒有太大差別。但是使用註解方式允許更細粒度的自動裝配,我們可以選擇性地標註某一屬性來對應其應用自動裝配。

Spring容器預設禁止註解裝配。所以,在使用基於註解的自動裝配前,我們需要在Spring配置中啟用它。最簡單的啟用方式是使用Spring的context名稱空間配置中的<context:annotation-config />元素。

<context:annotation-config />元素告訴Spring我們打算使用基於註解的自動裝配。一旦配置完成,我們就可以對程式碼添加註解,標識Spring應該為屬性、方法和構造器進行自動裝配。

Spring3支援幾種不同的用於自動裝配的註解:

  • Spring自帶的@Autowired註解
  • JSR-330的@Inject註解
  • JSR-250的@Resource註解

使用@Autowired

假設我們希望使用@Autowired讓Spring自動裝配樂器演奏家(Instrumentalist)Bean的instrument屬性。則可以對setInstrument()方法進行標註,如下

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

當Spring發現我們對setInstrument()方法使用了@Autowired註解時,Spring就會嘗試對該方法執行byType自動裝配。@Autowired也可以用於裝配Bean的引用和標註構造器。

可選的自動裝配

預設情況下,@Autowired具有強契約特徵,其所標註的屬性或引數必須是可裝配的。如果沒有Bean可以裝配到@Autowired所標註的屬性或引數中,自動裝配就會失敗(丟擲令人討厭的NoSuchBeanDefinitionException)。這可能是我們所期望的處理方式——當自動裝配無法完成時,讓Spring儘早失敗,遠勝於以後丟擲異常。

屬性不一定非要裝配,null值也是可以接受的。在這種場景下,可以通過設定@Autowired的required屬性為false來配置自動裝配是可選的。

@Autowired(required=false)
private Instrument instrument;

在這裡,Spring將嘗試裝配instrument屬性,但是如果沒有查詢到與之匹配的型別為Instrument的Bean,應用就不會發生任何問題,而instrument屬性的值會設定為null。

限定歧義性的依賴

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

例如,為了確保Spirng為eddie Bean選擇吉他(guitar)來演奏,即使有其他Bean也可以裝配到instrument屬性中,但我們可以使用@Qualifier來明確指定名為guitar的Bean:

@Autowired
@Qualifier("guitar")
private Instrument instrument;

如上所示,@Qualifier註解將嘗試注入ID為guitar的Bean。

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

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

例如,我們有一個KnifeJuggler類需要注入一個或多個Knife的例項。假設Knife Bean的作用域宣告為prototype,下面的KnifeJuggler的構造器將獲得5個Knife Bean:

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());
    }
}

KnifeJuggler將獲得一個Provider,而不是在構造器中獲得一個Knife例項。這個時候,只有provider被注入進去;在呼叫provider的get()方法之前,實際的Knife物件並沒有被注入。在這個示例中,get()方法被呼叫了5次。因為Knife Bean的作用域為prototype,所以knife的Set集合將被賦予5個不同的Knife物件。

限定@Inject所標註的屬性

@Named註解的工作方式非常類似於Spring的@Qualifier,正如我們在這裡所看到的:

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

在註解注入中使用表示式

@Value註解儘管易於使用,但我們很快就會發現,它同樣具有威力。我們可以通過@Value直接標註某個屬性、方法或者方法引數,並傳入一個String型別的表示式來裝配屬性。例如:

@Value("Eruption")
private String song;

在這裡,我們為String型別的屬性裝配了一個String型別的值。但是傳入@Value的String型別的引數只是一個表示式——它的結算結果可以是任意型別,因此@Value能夠標註任意型別的屬性。

自動檢測Bean

<context:component-scan>元素除了完成了與<context:annotation-config>一樣的工作,還允許Spring自動檢測Bean和定義Bean。

<context:component-scan>元素會掃描指定的包及其所有子包,並查詢能夠自動註冊為Spring Bean的類。base-package屬性標識了<context:component-scan>元素所掃描的包。

為自動檢測標註Bean

預設情況下,<context:component-scan >查詢使用構造器(stereotype)註解所有標註的類,這些特殊的註解如下:

  • @Component ——通用的構造型註解,標識該類為Spring元件。
  • @Controller——標識將該類定義為SpringMVC controller。
  • @Repository——標識將該類定義為資料倉庫
  • @Service——標識將該類定義為服務。
  • 使用@Component標註的任意自定義註解。

過濾元件掃描

通過為<context:component-scan>配置<context:include-filter>和/或者<context:exclude-filter>子元素,我們可以隨意調整掃描行為。

過濾器型別 描述
annotation 過濾器掃描使用指定註解所標註的那些類,通過expression屬性指定要掃描的註解
assignable 過濾器掃描派生於expression屬性所指定型別的那些類
aspectj 過濾器掃描與expression屬性所指定的AspectJ表示式所匹配的那些類
custom 使用自定義的org.springframework.core.type.TypeFilter實現類,該類由expression
regex 過濾器掃描類的名稱與expression屬性所指定的正則表示式匹配的那些類