Spring註解詳解1——【轉】

  概述java

註釋配置相對於 XML 配置具備不少的優點:程序員

  • 它能夠充分利用 Java 的反射機制獲取類結構信息,這些信息能夠有效減小配置的工做。如使用 JPA 註釋配置 ORM 映射時,咱們就不須要指定 PO 的屬性名、類型等信息,若是關係表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務屬性映射信息——由於這些信息均可以經過 Java 反射機制獲取。spring

  • 註釋和 Java 代碼位於一個文件中,而 XML 配置採用獨立的配置文件,大多數配置信息在程序開發完成後都不會調整,若是配置信息和 Java 代碼放在一塊兒,有助於加強程序的內聚性。而採用獨立的 XML 配置文件,程序員在編寫一個功能時,每每須要在程序文件和配置文件中不停切換,這種思惟上的不連貫會下降開發效率。ide

所以在不少狀況下,註釋配置比 XML 配置更受歡迎,註釋配置有進一步流行的趨勢。Spring 2.5 的一大加強就是引入了不少註釋類,如今您已經可使用註釋配置完成大部分 XML 配置的功能。在這篇文章裏,咱們將向您講述使用註釋進行 Bean 定義和依賴注入的內容。函數



   原來咱們是怎麼作的測試

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

Office 僅有一個屬性:this

   清單 1. Office.javaspa

      

package com.baobaotao;
public class Office {
    private String officeNo =」001」;

    //省略 get/setter

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


Car 擁有兩個屬性:code

清單 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 的信息:

清單 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);
    }
}

   

    使用 @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@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將@Resource 註釋的 name 屬性解析爲 Bean 的名字,而 type 屬性則解析爲 Bean 的類型。因此若是使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。若是既不指定 name 也不指定 type 屬性,這時將經過反射機制使用 byType 自動注入策略。

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 的方法將在類銷燬以前調用。

相關文章
相關標籤/搜索