Spring註解詳解

概述

註釋配置相對於 XML 配置具有很多的優勢:

  • 它可以充分利用 Java 的反射機制獲取類結構信息,這些信息可以有效減少配置的工作。如使用 JPA 註釋配置 ORM 映射時,我們就不需要指定 PO 的屬性名、類型等信息,如果關係表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務屬性映射信息——因爲這些信息都可以通過 Java 反射機制獲取。
  • 註釋和 Java 代碼位於一個文件中,而 XML 配置採用獨立的配置文件,大多數配置信息在程序開發完成後都不會調整,如果配置信息和 Java 代碼放在一起,有助於增強程序的內聚性。而採用獨立的 XML 配置文件,程序員在編寫一個功能時,往往需要在程序文件和配置文件中不停切換,這種思維上的不連貫會降低開發效率。

因此在很多情況下,註釋配置比 XML 配置更受歡迎,註釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多註釋類,現在您已經可以使用註釋配置完成大部分 XML 配置的功能。在這篇文章裏,我們將向您講述使用註釋進行 Bean 定義和依賴注入的內容。


原來我們是怎麼做的

在使用註釋配置之前,先來回顧一下傳統上是如何配置 Bean 並完成 Bean 之間依賴關係的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置爲 Bean:

Office 僅有一個屬性:


清單 1. Office.java
                
package com.baobaotao;
public class Office {
    private String officeNo =”001”;

    //省略 get/setter

    @Override
    public String toString() {
        return "officeNo:" + officeNo;
    }
}

Car 擁有兩個屬性:


清單 2. Car.java
                
package com.baobaotao;

public class Car {
    private String brand;
    private double price;

    // 省略 get/setter

    @Override
    public String toString() {
        return "brand:" + brand + "," + "price:" + price;
    }
}

Boss 擁有 Office 和 Car 類型的兩個屬性:


清單 3. Boss.java
                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

    // 省略 get/setter

    @Override
    public String toString() {
        return "car:" + car + "\n" + "office:" + office;
    }
}

我們在 Spring 容器中將 Office 和 Car 聲明爲 Bean,並注入到 Boss Bean 中:下面是使用傳統 XML 完成這個工作的配置文件 beans.xml:


清單 4. beans.xml 將以上三個類配置成 Bean
                
<?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-2.5.xsd">
    <bean id="boss" class="com.baobaotao.Boss">
        <property name="car" ref="car"/>
        <property name="office" ref="office" />
    </bean>
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="002"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

當我們運行以下代碼時,控制檯將正確打出 boss 的信息:

(此處也可以使用Juint編寫測試類)
清單 5. 測試類:AnnoIoCTest.java
                
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ApplicationContext ctx = 
		    new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
    }
//編寫測試類
@Test
public void test() {

String[] locations = {"beans.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(locations);
Boss boss = (Boss) ctx.getBean("boss");
System.out.println(boss);
}



這說明 Spring 容器已經正確完成了 Bean 創建和裝配的工作。


使用 @Autowired 註釋

Spring 2.5 引入了 @Autowired 註釋,它可以對類成員變量、方法及構造函數進行標註,完成自動裝配的工作。來看一下使用@Autowired 進行成員變量自動注入的代碼:


清單 6. 使用 @Autowired 註釋的 Boss.java
                
package com.baobaotao;
import org.springframework.beans.factory.annotation.Autowired;

public class Boss {

    @Autowired
    private Car car;

    @Autowired
    private Office office;

    …
}

Spring 通過一個 BeanPostProcessor 對 @Autowired 進行解析,所以要讓@Autowired 起作用必須事先在 Spring 容器中聲明AutowiredAnnotationBeanPostProcessor Bean。


清單 7. 讓 @Autowired 註釋工作起來
                
<?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-2.5.xsd">

    <!-- 該 BeanPostProcessor 將自動起作用,對標註 @Autowired 的 Bean 進行自動注入 -->
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/>

    <!-- 移除 boss Bean 的屬性注入配置的信息 -->
    <bean id="boss" class="com.baobaotao.Boss"/>
 
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="001"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

這樣,當 Spring 容器啓動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有@Autowired 註釋時就找到和其匹配(默認按類型匹配)的 Bean,並注入到對應的地方中去。

按照上面的配置,Spring 將直接採用 Java 反射機制對 Boss 中的 car 和 office 這兩個私有成員變量進行自動注入。所以對成員變量使用@Autowired後,您大可將它們的 setter 方法(setCar() 和 setOffice())從 Boss 中刪除。

當然,您也可以通過 @Autowired 對方法或構造函數進行標註,來看下面的代碼:


清單 8. 將 @Autowired 註釋標註在 Setter 方法上
                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;

     @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
 
    @Autowired
    public void setOffice(Office office) {
        this.office = office;
    }
    …
}

這時,@Autowired 將查找被標註的方法的入參類型的 Bean,並調用方法自動注入這些 Bean。而下面的使用方法則對構造函數進行標註:


清單 9. 將 @Autowired 註釋標註在構造函數上
                
package com.baobaotao;

public class Boss {
    private Car car;
    private Office office;
 
    @Autowired
    public Boss(Car car ,Office office){
        this.car = car;
        this.office = office ;
    }
 
    …
}

由於 Boss() 構造函數有兩個入參,分別是 car 和 office@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作爲Boss(Car car ,Office office)的入參來創建 Boss Bean。


當候選 Bean 數目不爲 1 時的應對方法

在默認情況下使用 @Autowired 註釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將拋出BeanCreationException 異常,並指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:


清單 10. 候選 Bean 數目爲 0 時
                
<?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-2.5.xsd ">
 
    <bean class="org.springframework.beans.factory.annotation.
        AutowiredAnnotationBeanPostProcessor"/> 

    <bean id="boss" class="com.baobaotao.Boss"/>

    <!-- 將 office Bean 註釋掉 -->
    <!-- <bean id="office" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
    </bean>-->

    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

由於 office Bean 被註釋掉了,所以 Spring 容器中將沒有類型爲 Office 的 Bean 了,而 Boss 的office 屬性標註了 @Autowired,當啓動 Spring 容器時,異常就產生了。

當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等於告訴 Spring:在找不到匹配 Bean 時也不報錯。來看一下具體的例子:


清單 11. 使用 @Autowired(required = false)
                
package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

public class Boss {

    private Car car;
    private Office office;

    @Autowired
    public void setCar(Car car) {
        this.car = car;
    }
    @Autowired(required = false)
    public void setOffice(Office office) {
        this.office = office;
    }
    …
}

當然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發期或測試期碰到(如爲了快速啓動 Spring 容器,僅引入一些模塊的 Spring 配置文件),所以@Autowired(required = false) 會很少用到。

和找不到一個類型匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啓動時也會拋出 BeanCreationException 異常。來看下面的例子:


清單 12. 在 beans.xml 中配置兩個 Office 類型的 Bean
                
… 
<bean id="office" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>
<bean id="office2" class="com.baobaotao.Office">
    <property name="officeNo" value="001"/>
</bean>
…

我們在 Spring 容器中配置了兩個類型爲 Office 類型的 Bean,當對 Boss 的 office 成員變量進行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發生了。

Spring 允許我們通過 @Qualifier 註釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:


清單 13. 使用 @Qualifier 註釋指定注入 Bean 的名稱
                
@Autowired
public void setOffice(@Qualifier("office")Office office) {
    this.office = office;
}

@Qualifier("office") 中的 office 是 Bean 的名稱,所以 @Autowired 和@Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。@Autowired 可以對成員變量、方法以及構造函數進行註釋,而@Qualifier 的標註對象是成員變量、方法入參、構造函數入參。正是由於註釋對象的不同,所以 Spring 不將 @Autowired 和@Qualifier 統一成一個註釋類。下面是對成員變量和構造函數入參進行註釋的代碼:

對成員變量進行註釋:


清單 14. 對成員變量使用 @Qualifier 註釋
                
public class Boss {
    @Autowired
    private Car car;
 
    @Autowired
    @Qualifier("office")
    private Office office;
    …
}

對構造函數入參進行註釋:


清單 15. 對構造函數變量使用 @Qualifier 註釋
                
public class Boss {
    private Car car;
    private Office office;

    @Autowired
    public Boss(Car car , @Qualifier("office")Office office){
        this.car = car;
        this.office = office ;
	}
}

@Qualifier 只能和 @Autowired 結合使用,是對 @Autowired 有益的補充。一般來講,@Qualifier 對方法簽名中入參進行註釋會降低代碼的可讀性,而對成員變量註釋則相對好一些。


使用 JSR-250 的註釋

Spring 不但支持自己定義的 @Autowired 的註釋,還支持幾個由 JSR-250 規範定義的註釋,它們分別是 @Resource@PostConstruct 以及 @PreDestroy

@Resource

@Resource 的作用相當於 @Autowired,只不過 @Autowired 按 byType 自動注入,面@Resource 默認按 byName 自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將@Resource 註釋的 name 屬性解析爲 Bean 的名字,而 type 屬性則解析爲 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。

Resource 註釋類位於 Spring 發佈包的 lib/j2ee/common-annotations.jar 類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用@Resource 的例子:


清單 16. 使用 @Resource 註釋的 Boss.java
                
package com.baobaotao;

import javax.annotation.Resource;

public class Boss {
    // 自動注入類型爲 Car 的 Bean
    @Resource
    private Car car;

    // 自動注入 bean 名稱爲 office 的 Bean
    @Resource(name = "office")
    private Office office;
}

一般情況下,我們無需使用類似於 @Resource(type=Car.class) 的註釋方式,因爲 Bean 的類型信息可以通過 Java 反射從代碼中獲取。

要讓 JSR-250 的註釋生效,除了在 Bean 類中標註這些註釋外,還需要在 Spring 容器中註冊一個負責處理這些註釋的 BeanPostProcessor

<bean 
  class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

CommonAnnotationBeanPostProcessor 實現了 BeanPostProcessor 接口,它負責掃描使用了 JSR-250 註釋的 Bean,並對它們進行相應的操作。

@PostConstruct 和 @PreDestroy

Spring 容器中的 Bean 是有生命週期的,Spring 允許在 Bean 在初始化完成後以及 Bean 銷燬前執行特定的操作,您既可以通過實現 InitializingBean/DisposableBean 接口來定製初始化之後 / 銷燬之前的操作方法,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之後 / 銷燬之前調用的操作方法。關於 Spring 的生命週期,筆者在《精通 Spring 2.x—企業應用開發精解》第 3 章進行了詳細的描述,有興趣的讀者可以查閱。

JSR-250 爲初始化之後/銷燬之前方法的指定定義了兩個註釋類,分別是 @PostConstruct 和 @PreDestroy,這兩個註釋只能應用於方法上。標註了 @PostConstruct 註釋的方法將在類實例化後調用,而標註了 @PreDestroy 的方法將在類銷燬之前調用。


清單 17. 使用 @PostConstruct 和 @PreDestroy 註釋的 Boss.java
                
package com.baobaotao;

import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class Boss {
    @Resource
    private Car car;

    @Resource(name = "office")
    private Office office;

    @PostConstruct
    public void postConstruct1(){
        System.out.println("postConstruct1");
    }

    @PreDestroy
    public void preDestroy1(){
        System.out.println("preDestroy1"); 
    }
    …
}

您只需要在方法前標註 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化後或銷燬之前被 Spring 容器執行了。

我們知道,不管是通過實現 InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的init-method/destroy-method 屬性進行配置,都只能爲 Bean 指定一個初始化 / 銷燬的方法。但是使用 @PostConstruct 和 @PreDestroy 註釋卻可以指定多個初始化 / 銷燬方法,那些被標註 @PostConstruct 或@PreDestroy 註釋的方法都會在初始化 / 銷燬時被執行。

通過以下的測試代碼,您將可以看到 Bean 的初始化 / 銷燬方法是如何被執行的:


清單 18. 測試類代碼
                
package com.baobaotao;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnoIoCTest {

    public static void main(String[] args) {
        String[] locations = {"beans.xml"};
        ClassPathXmlApplicationContext ctx = 
            new ClassPathXmlApplicationContext(locations);
        Boss boss = (Boss) ctx.getBean("boss");
        System.out.println(boss);
        ctx.destroy();// 關閉 Spring 容器,以觸發 Bean 銷燬方法的執行
    }
}

這時,您將看到標註了 @PostConstruct 的 postConstruct1() 方法將在 Spring 容器啓動時,創建Boss Bean 的時候被觸發執行,而標註了 @PreDestroy註釋的 preDestroy1() 方法將在 Spring 容器關閉前銷燬Boss Bean 的時候被觸發執行。


使用 <context:annotation-config/> 簡化配置

Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對註釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道註釋本身是不會做任何事情的,它僅提供元數據信息。要使元數據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。

而我們前面所介紹的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是處理這些註釋元數據的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 爲我們提供了一種方便的註冊這些BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:


清單 19. 調整 beans.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"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
 
    <context:annotation-config/> 

    <bean id="boss" class="com.baobaotao.Boss"/>
    <bean id="office" class="com.baobaotao.Office">
        <property name="officeNo" value="001"/>
    </bean>
    <bean id="car" class="com.baobaotao.Car" scope="singleton">
        <property name="brand" value=" 紅旗 CA72"/>
        <property name="price" value="2000"/>
    </bean>
</beans>

<context:annotationconfig/> 將隱式地向 Spring 容器註冊AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor 以及equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。

在配置文件中使用 context 命名空間之前,必須在 <beans> 元素中聲明 context 命名空間。


使用 @Component

雖然我們可以通過 @Autowired 或 @Resource 在 Bean 類中使用自動注入功能,但是 Bean 還是在 XML 文件中通過 <bean> 進行定義 —— 也就是說,在 XML 配置文件中定義 Bean,通過@Autowired 或 @Resource 爲 Bean 的成員變量、方法入參或構造函數入參提供自動注入的功能。能否也通過註釋定義 Bean,從 XML 配置文件中完全移除 Bean 定義的配置呢?答案是肯定的,我們通過 Spring 2.5 提供的@Component 註釋就可以達到這個目標了。

爲什麼 @Repository 只能標註在 DAO 類上呢?這是因爲該註解的作用不只是將類識別爲 Bean,同時它還能將所標註的類中拋出的數據訪問異常封裝爲 Spring 的數據訪問異常類型。 Spring 本身提供了一個豐富的並且是與具體的數據訪問技術無關的數據訪問異常結構,用於封裝不同的持久層框架拋出的異常,使得異常獨立於底層的框架。

Spring 2.5 在 @Repository 的基礎上增加了功能類似的額外三個註解:@Component、@Service、@Constroller,它們分別用於軟件系統的不同層次:

  • @Component 是一個泛化的概念,僅僅表示一個組件 (Bean) ,可以作用在任何層次。
  • @Service 通常作用在業務層,但是目前該功能與 @Component 相同。
  • @Constroller 通常作用在控制層,但是目前該功能與 @Component 相同。

通過在類上使用 @Repository、@Component、@Service 和 @Constroller 註解,Spring 會自動創建相應的 BeanDefinition 對象,並註冊到 ApplicationContext 中。這些類就成了 Spring 受管組件。這三個註解除了作用於不同軟件層次的類,其使用方式與 @Repository 是完全相同的。

 

 

下面,我們完全使用註釋定義 Bean 並完成 Bean 之間裝配:


清單 20. 使用 @Component 註釋的 Car.java
                
package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Car {
    …
}

僅需要在類定義處,使用 @Component 註釋就可以將一個類定義了 Spring 容器中的 Bean。下面的代碼將 Office 定義爲一個 Bean:


清單 21. 使用 @Component 註釋的 Office.java
                
package com.baobaotao;

import org.springframework.stereotype.Component;

@Component
public class Office {
    private String officeNo = "001";
    …
}

這樣,我們就可以在 Boss 類中通過 @Autowired 注入前面定義的 Car 和 Office Bean 了。


清單 22. 使用 @Component 註釋的 Boss.java
                
package com.baobaotao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component("boss")
public class Boss {
    @Autowired
    private Car car;

    @Autowired
    private Office office;
    …
}

@Component 有一個可選的入參,用於指定 Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義爲“boss”。一般情況下,Bean 都是 singleton 的,需要注入 Bean 的地方僅需要通過 byType 策略就可以自動注入了,所以大可不必指定 Bean 的名稱。

在使用 @Component 註釋後,Spring 容器必須啓用類掃描機制以啓用註釋驅動 Bean 定義和註釋驅動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進行了擴展,提供了這一功能,請看下面的配置:


清單 23. 簡化版的 beans.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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:component-scan base-package="com.baobaotao"/>
</beans>

這裏,所有通過 <bean> 元素定義 Bean 的配置內容已經被移除,僅需要添加一行 <context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以註釋形式存在罷了)。<context:component-scan/> 的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。

<context:component-scan/> 還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:


表 1. 掃描過濾方式
過濾器類型 說明
註釋 假如 com.baobaotao.SomeAnnotation 是一個註釋類,我們可以將使用該註釋的類過濾出來。
類名指定 通過全限定類名進行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。
正則表達式 通過正則表達式定義過濾的類,如下所示: com\.baobaotao\.Default.*
AspectJ 表達式 通過 AspectJ 表達式定義過濾的類,如下所示: com. baobaotao..*Service+

下面是一個簡單的例子:

<context:component-scan base-package="com.baobaotao">
    <context:include-filter type="regex" 
        expression="com\.baobaotao\.service\..*"/>
    <context:exclude-filter type="aspectj" 
        expression="com.baobaotao.util..*"/>
</context:component-scan>

值得注意的是 <context:component-scan/> 配置項不但啓用了對類包進行掃描以實施註釋驅動 Bean 定義的功能,同時還啓用了註釋驅動自動注入的功能(即還隱式地在內部註冊了AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此當使用 <context:component-scan/> 後,就可以將 <context:annotation-config/> 移除了。

默認情況下通過 @Component 定義的 Bean 都是 singleton 的,如果需要使用其它作用範圍的 Bean,可以通過@Scope 註釋來達到目標,如以下代碼所示:

@scopee


清單 24. 通過 @Scope 指定 Bean 的作用範圍
                
package com.baobaotao;
import org.springframework.context.annotation.Scope;
…
@Scope("prototype")
@Component("boss")
public class Boss {
    …
}

這樣,當從 Spring 容器中獲取 boss Bean 時,每次返回的都是新的實例了。


採用具有特殊語義的註釋

Spring 2.5 中除了提供 @Component 註釋外,還定義了幾個擁有特殊語義的註釋,它們分別是:@Repository@Service 和@Controller。在目前的 Spring 版本中,這 3 個註釋和 @Component 是等效的,但是從註釋類的命名上,很容易看出這 3 個註釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個註釋和@Component 相比沒有什麼新意,但 Spring 將在以後的版本中爲它們添加特殊的功能。所以,如果 Web 應用程序採用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別採用@Repository@Service 和 @Controller 對分層中的類進行註釋,而用@Component 對那些比較中立的類進行註釋。


註釋配置和 XML 配置的適用場合

是否有了這些 IOC 註釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點原因:

  • 註釋配置不一定在先天上優於 XML 配置。如果 Bean 的依賴關係是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那麼註釋配置優於 XML 配置;反之如果這種依賴關係會在部署時發生調整,XML 配置顯然又優於註釋配置,因爲註釋是對 Java 源代碼的調整,您需要重新改寫源代碼並重新編譯纔可以實施調整。
  • 如果 Bean 不是自己編寫的類(如 JdbcTemplateSessionFactoryBean 等),註釋配置將無法實施,此時 XML 配置是唯一可用的方式。
  • 註釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比於 @Transaction 事務註釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。

所以在實現應用中,我們往往需要同時使用註釋配置和 XML 配置,對於類級別且不會發生變動的配置可以優先考慮註釋配置;而對於那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 創建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。

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