使用Spring2.5的Autowired實現注釋型的IOC

轉載鏈接

使用Spring2.5的新特性--Autowired可以實現快速的自動注入,而無需在xml文檔里面添加bean的聲明,大大減少了xml文檔的維護潘懊。(偶喜歡這個功能狼电,因為偶對xml不感冒)。 以下是一個例子:
先編寫接口Man:

public interface Man {  
      public String sayHello();  
}  

然后寫Man的實現類Chinese和American:

@Service  
public class Chinese implements Man{  
    public String sayHello() {  
        return "I am Chinese!";  
    }  
}  
  
@Service  
public class American implements Man{  
    public String sayHello() {  
        return "I am American!";  
    }  
}  

@Service注釋表示定義一個bean盆均,自動根據bean的類名實例化一個首寫字母為小寫的bean塞弊,例如Chinese實例化為chinese,American實例化為american泪姨,如果需要自己改名字則:@Service("你自己改的bean名")游沿。

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/>  
      <context:component-scan base-package="testspring.main"/>  
</beans>

在spring的配置文件里面只需要加上<context:annotation-config/>和<context:component-scan base-package="需要實現注入的類所在包"/>,可以使用base-package="*"表示全部的類肮砾。

編寫主類測試:

@Service  
public class Main {  
    @Autowired  
    @Qualifier("chinese")  
    private Man man;  
  
    public static void main(String[] args) {  
        // TODO code application logic here  
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");  
        Main main = (Main) ctx.getBean("main");  
        System.out.println(main.getMan().sayHello());  
    }  
  
    public Man getMan() {  
        return man;  
    }  
}  

在Man接口前面標上@Autowired和@Qualifier注釋使得Man接口可以被容器注入诀黍,當Man接口存在兩個實現類的時候必須指定其中一個來注入,使用實現類首字母小寫的字符串來注入仗处。否則可以省略眯勾,只寫@Autowired

注釋配置相對于 XML 配置具有很多的優(yōu)勢:

它可以充分利用 Java 的反射機制獲取類結構信息,這些信息可以有效減少配置的工作婆誓。如使用 JPA 注釋配置 ORM 映射時吃环,我們就不需要指定 PO 的屬性名、類型等信息洋幻,如果關系表字段和 PO 屬性名郁轻、類型都一致,您甚至無需編寫任務屬性映射信息——因為這些信息都可以通過 Java 反射機制獲取文留。
注釋和 Java 代碼位于一個文件中范咨,而 XML 配置采用獨立的配置文件故觅,大多數配置信息在程序開發(fā)完成后都不會調整,如果配置信息和 Java 代碼放在一起渠啊,有助于增強程序的內聚性输吏。而采用獨立的 XML 配置文件,程序員在編寫一個功能時替蛉,往往需要在程序文件和配置文件中不停切換贯溅,這種思維上的不連貫會降低開發(fā)效率。
因此在很多情況下躲查,注釋配置比 XML 配置更受歡迎它浅,注釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多注釋類镣煮,現在您已經可以使用注釋配置完成大部分 XML 配置的功能姐霍。在這篇文章里,我們將向您講述使用注釋進行 Bean 定義和依賴注入的內容典唇。

原來我們是怎么做的

在使用注釋配置之前镊折,先來回顧一下傳統(tǒng)上是如何配置 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 中:下面是使用傳統(tǒng) 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);  
    }  
}  

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

使用 @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矛双,當發(fā)現 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) 的入參來創(chuàng)建 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 的嚣伐,使用了自動注入而又允許不注入的情況一般僅會在開發(fā)期或測試期碰到(如為了快速啟動 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壳影,因此異常發(fā)生了拱层。

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 統(tǒng)一成一個注釋類怎栽。下面是對成員變量和構造函數入參進行注釋的代碼:

對成員變量進行注釋:

清單 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 規(guī)范定義的注釋由桌,它們分別是 @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 發(fā)布包的 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 銷毀前執(zhí)行特定的操作,您既可以通過實現 InitializingBean/DisposableBean 接口來定制初始化之后 / 銷毀之前的操作方法嘉抓,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 / 銷毀之前調用的操作方法索守。關于 Spring 的生命周期,筆者在《精通 Spring 2.x—企業(yè)應用開發(fā)精解》第 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 容器執(zhí)行了。

我們知道焰枢,不管是通過實現 InitializingBean/DisposableBean 接口蚓峦,還是通過 <bean> 元素的 init-method/destroy-method 屬性進行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法济锄。但是使用 @PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法暑椰,那些被標注 @PostConstruct 或 @PreDestroy 注釋的方法都會在初始化 / 銷毀時被執(zhí)行。

通過以下的測試代碼荐绝,您將可以看到 Bean 的初始化 / 銷毀方法是如何被執(zhí)行的:

清單 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 容器一汽,以觸發(fā) Bean 銷毀方法的執(zhí)行  
    }  
}  

這時,您將看到標注了 @PostConstruct 的 postConstruct1() 方法將在 Spring 容器啟動時低滩,創(chuàng)建 Boss Bean 的時候被觸發(fā)執(zhí)行召夹,而標注了 @PreDestroy 注釋的 preDestroy1() 方法將在 Spring 容器關閉前銷毀 Boss Bean 的時候被觸發(fā)執(zhí)行。

使用 <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 容器注冊 AutowiredAnnotationBeanPostProcessor曲梗、CommonAnnotationBeanPostProcessor赞警、PersistenceAnnotationBeanPostProcessor 以及 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 注釋就可以達到這個目標了遭庶。

下面,我們完全使用注釋定義 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 注釋來達到目標粘勒,如以下代碼所示:

清單 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 個注釋分別和持久層、業(yè)務層和控制層(Web 層)相對應谤逼。雖然目前這 3 個注釋和 @Component 相比沒有什么新意贵扰,但 Spring 將在以后的版本中為它們添加特殊的功能。所以流部,如果 Web 應用程序采用了經典的三層分層結構的話戚绕,最好在持久層、業(yè)務層和控制層分別采用 @Repository枝冀、@Service 和 @Controller 對分層中的類進行注釋舞丛,而用 @Component 對那些比較中立的類進行注釋。

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

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

注釋配置不一定在先天上優(yōu)于 XML 配置跨晴。如果 Bean 的依賴關系是固定的,(如 Service 使用了哪幾個 DAO 類)片林,這種配置信息不會在部署時發(fā)生調整端盆,那么注釋配置優(yōu)于 XML 配置怀骤;反之如果這種依賴關系會在部署時發(fā)生調整,XML 配置顯然又優(yōu)于注釋配置焕妙,因為注釋是對 Java 源代碼的調整蒋伦,您需要重新改寫源代碼并重新編譯才可以實施調整。
如果 Bean 不是自己編寫的類(如 JdbcTemplate焚鹊、SessionFactoryBean 等)痕届,注釋配置將無法實施,此時 XML 配置是唯一可用的方式末患。
注釋配置往往是類級別的研叫,而 XML 配置則可以表現得更加靈活。比如相比于 @Transaction 事務注釋璧针,使用 aop/tx 命名空間的事務配置更加靈活和簡單嚷炉。
所以在實現應用中,我們往往需要同時使用注釋配置和 XML 配置探橱,對于類級別且不會發(fā)生變動的配置可以優(yōu)先考慮注釋配置申屹;而對于那些第三方類以及容易發(fā)生調整的配置則應優(yōu)先考慮使用 XML 配置。Spring 會在具體實施 Bean 創(chuàng)建和 Bean 注入之前將這兩種配置方式的元信息融合在一起隧膏。

小結

Spring 在 2.1 以后對注釋配置提供了強力的支持哗讥,注釋配置功能成為 Spring 2.5 的最大的亮點之一。合理地使用 Spring 2.5 的注釋配置胞枕,可以有效減少配置的工作量忌栅,提高程序的內聚性。但是這并不意味著傳統(tǒng) XML 配置將走向消亡曲稼,在第三方類 Bean 的配置索绪,以及那些諸如數據源、緩存池贫悄、持久層操作模板類瑞驱、事務管理等內容的配置上,XML 配置依然擁有不可替代的地位窄坦。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末唤反,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子鸭津,更是在濱河造成了極大的恐慌彤侍,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逆趋,死亡現場離奇詭異盏阶,居然都是意外死亡,警方通過查閱死者的電腦和手機闻书,發(fā)現死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門名斟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脑慧,“玉大人,你說我怎么就攤上這事砰盐∶铺唬” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵岩梳,是天一觀的道長囊骤。 經常有香客問我,道長冀值,這世上最難降的妖魔是什么淘捡? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮池摧,結果婚禮上焦除,老公的妹妹穿的比我還像新娘。我一直安慰自己作彤,他們只是感情好膘魄,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著竭讳,像睡著了一般创葡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绢慢,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天灿渴,我揣著相機與錄音,去河邊找鬼胰舆。 笑死骚露,一個胖子當著我的面吹牛,可吹牛的內容都是我干的缚窿。 我是一名探鬼主播棘幸,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倦零!你這毒婦竟也來了误续?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扫茅,失蹤者是張志新(化名)和其女友劉穎蹋嵌,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體葫隙,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡栽烂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愕鼓。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡钙态,死狀恐怖慧起,靈堂內的尸體忽然破棺而出菇晃,到底是詐尸還是另有隱情,我是刑警寧澤蚓挤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布磺送,位于F島的核電站,受9級特大地震影響灿意,放射性物質發(fā)生泄漏估灿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一缤剧、第九天 我趴在偏房一處隱蔽的房頂上張望馅袁。 院中可真熱鬧,春花似錦荒辕、人聲如沸汗销。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弛针。三九已至,卻和暖如春李皇,著一層夾襖步出監(jiān)牢的瞬間削茁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工掉房, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茧跋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓卓囚,卻偏偏與公主長得像厌衔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捍岳,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容