Sealyu

          --- 博客已遷移至: http://www.sealyu.com/blog

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            618 隨筆 :: 87 文章 :: 225 評論 :: 0 Trackbacks
          基于注釋(Annotation)的配置有越來越流行的趨勢,Spring 2.5 順應這種趨勢,提供了完全基于注釋配置 Bean、裝配 Bean 的功能,您可以
          使用基于注釋的 Spring IoC 替換原來基于 XML 的配置。本文通過實例詳細講述了 Spring 2.5 基于注釋 IoC 功能的使用。
          概述
          注釋配置相對于 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 的信息:


          清單 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 創建和裝配的工作。

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

          如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean 等),注釋配置將無法實施,此時 XML 配置是唯一可用的方式。
          注釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比于 @Transaction 事務注釋,使用 aop/tx 命名空間的事務配置更加靈活
          和簡單。
          所以在實現應用中,我們往往需要同時使用注釋配置和 XML 配置,對于類級別且不會發生變動的配置可以優先考慮注釋配置;而對于那些第三方類以及
          容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 創建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。

          小結

          Spring 在 2.1 以后對注釋配置提供了強力的支持,注釋配置功能成為 Spring 2.5 的最大的亮點之一。合理地使用 Spring 2.5 的注釋配置,
          可以有效減少配置的工作量,提高程序的內聚性。但是這并不意味著傳統 XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數據源、
          緩存池、持久層操作模板類、事務管理等內容的配置上,XML 配置依然擁有不可替代的地位。
          spring 事務管理(Transaction) 例子
           
           
          ##Transaction) 例子  
          傳統上, J2EE開發者有兩種事務管理選擇,全局和本地事務。Spring框架對事務管理的支持極大地改變傳統上認為J2EE應用需要應用服務器。這種
          改變不單是僅僅為了通過EJB來使用生命式事務而使用應用服務器。事實上,即使你的應用服務器有強大的JTA功能,Spring框架的聲明式事務提供了
          比EJB CMT(聲明式事務)更強大,更高效的編程模型。一般來說,只有支持多個事務資源,才會需要應用服務器的JTA功能,而大多數應用不需要能夠
          處理跨多種資源。最重要的一點,使用Spring,你可以選擇何時把你的應用遷移到全功能的應用服務器。使用Spring不需要像以前一樣用編碼實現本地
          事務代替EJB CMT或JTA,現在只需要改配置問價,而不必改代碼。
          一. Spring事務管理

            1. Spring 編程式事務管理(programmatic transaction management)
             DefaultTransactionDefinition def = new DefaultTransactionDefinition();
          TransactionStatus status = transactionManager.getTransaction(def);
          try {
          JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

          jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)");

          jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)");
          jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)");

          } catch (DataAccessException ex) {

          transactionManager.rollback(status); // 也可以執行status.setRollbackOnly();
                  throw ex;
          }
          transactionManager.commit(status);

          見:http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=2398

            2. Spring宣告式事務管理(declarative transaction management)主要是在spring的配置文件中設置

              

              <bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">



          <property name="transactionManager">



          <ref bean="transactionManager"/>



          </property>



          <property name="target">



          <ref bean="userDAO"/>



          </property>



          <property name="transactionAttributes">



          <props>



          <prop key="insert*">PROPAGATION_REQUIRED</prop>



          </props>



          </property>       



          </bean>

          見:http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=2402
          二. Spring的@Transcation的例子

          1. class 中

          @Component //注釋就可以將該類定義了 Spring 容器中的 Bean

          public Class UserDaoImpl implements IUserDao {
          @Override
          //標志updateUsers()為Transactional    
          @Transactional(
          propagation = Propagation.REQUIRED,
          isolation = Isolation.DEFAULT,
          rollbackFor = Exception.class
          )
          public void updateUsers(List<User> users){
          for(User user :users){
          //邏輯
          }
          }
          }
          注意: 事務的屬性和基本概念



          Required : 如果在一個事務中調用,就將該方法加到此事務中,如果沒有啟動事務,就創建新事務



          RequiredNew : 不管當前有沒有事務,都啟動新事務,如果有,會被掛起,直到此方法結束



          NotSupported : 不能在事務中執行此方法,如果有事務,會被掛起,直到此方法結束 



          Supports : 如果有當前事務,此方法回加到當前事務,如果沒有,容器不會啟動新事務



          Mandatory : 必須在事務中執行此方法,否則會拋出異常 : TransactionRequiredException



          Never : 必須不在事務中調用此方法,否則拋出RemoteException(遠程調用)或EJBException(本地調用)
                  2.spring配置文件加入:
                     .....................
                      <tx:annotation-driven/>
                    .....................

                注意: webSerivces 是不支持事務的,如果使用事務,必須在下層的Object中實現,例如在Service層或者是Dao層實現。

          1. 使用Spring注解來注入屬性
          1.1. 使用注解以前我們是怎樣注入屬性的
          類的實現:
          Java代碼
          public class UserManagerImpl implements UserManager {  
              private UserDao userDao;  
              public void setUserDao(UserDao userDao) {  
                  this.userDao = userDao;  
              }  
              ...  


          public class UserManagerImpl implements UserManager {
          private UserDao userDao;
          public void setUserDao(UserDao userDao) {
          this.userDao = userDao;
          }
          ...
          }

          配置文件:
          Java代碼
          <bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl">  
              <property name="userDao" ref="userDao" />  
          </bean>  
          <bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl">  
              <property name="sessionFactory" ref="mySessionFactory" />  
          </bean> 

          <bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl">
          <property name="userDao" ref="userDao" />
          </bean>
          <bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl">
          <property name="sessionFactory" ref="mySessionFactory" />
          </bean>


          1.2. 引入@Autowired注解(不推薦使用,建議使用@Resource)
          類的實現(對成員變量進行標注)
          Java代碼
          public class UserManagerImpl implements UserManager {  
              @Autowired 
              private UserDao userDao;  
              ...  


          public class UserManagerImpl implements UserManager {
          @Autowired
          private UserDao userDao;
          ...
          }

          或者(對方法進行標注)
          Java代碼
          public class UserManagerImpl implements UserManager {  
              private UserDao userDao;  
              @Autowired 
              public void setUserDao(UserDao userDao) {  
                  this.userDao = userDao;  
              }  
              ...  


          public class UserManagerImpl implements UserManager {
          private UserDao userDao;
          @Autowired
          public void setUserDao(UserDao userDao) {
          this.userDao = userDao;
          }
          ...
          }
          配置文件
          Java代碼
          <bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl" />  
          <bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl">  
              <property name="sessionFactory" ref="mySessionFactory" />  
          </bean> 

          <bean id="userManagerImpl" class="com.kedacom.spring.annotation.service.UserManagerImpl" />
          <bean id="userDao" class="com.kedacom.spring.annotation.persistence.UserDaoImpl">
          <property name="sessionFactory" ref="mySessionFactory" />
          </bean>

          @Autowired可以對成員變量、方法和構造函數進行標注,來完成自動裝配的工作。以上兩種不同實現方式中,@Autowired的標注位置不同,它們都會在Spring在初始化userManagerImpl這個bean時,自動裝配userDao這個屬性,區別是:第一種實現中,Spring會直接將UserDao類型的唯一一個bean賦值給userDao這個成員變量;第二種實現中,Spring會調用setUserDao方法來將UserDao類型的唯一一個bean裝配到userDao這個屬性。

          1.3. 讓@Autowired工作起來
          要使@Autowired能夠工作,還需要在配置文件中加入以下代碼
          Java代碼
          <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /> 

          <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />


          1.4. @Qualifier
          @Autowired是根據類型進行自動裝配的。在上面的例子中,如果當Spring上下文中存在不止一個UserDao類型的bean時,就會拋出BeanCreationException異常;如果Spring上下文中不存在UserDao類型的bean,也會拋出BeanCreationException異常。我們可以使用@Qualifier配合@Autowired來解決這些問題。
          1. 可能存在多個UserDao實例
          Java代碼
          @Autowired 
          public void setUserDao(@Qualifier("userDao") UserDao userDao) {  
              this.userDao = userDao;  


          @Autowired
          public void setUserDao(@Qualifier("userDao") UserDao userDao) {
          this.userDao = userDao;
          }

          這樣,Spring會找到id為userDao的bean進行裝配。
          2. 可能不存在UserDao實例

          Java代碼
          @Autowired(required = false)  
          public void setUserDao(UserDao userDao) {  
              this.userDao = userDao;  


          @Autowired(required = false)
          public void setUserDao(UserDao userDao) {
          this.userDao = userDao;
          }


          1.5. @Resource(JSR-250標準注解,推薦使用它來代替Spring專有的@Autowired注解)
          Spring 不但支持自己定義的@Autowired注解,還支持幾個由JSR-250規范定義的注解,它們分別是@Resource、@PostConstruct以及@PreDestroy。
          @Resource的作用相當于@Autowired,只不過@Autowired按byType自動注入,而@Resource默認按byName自動注入罷了。@Resource有兩個屬性是比較重要的,分別是name和type,Spring將 @Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以如果使用name屬性,則使用byName的自 動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動 注入策略。
          @Resource裝配順序

          如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
          如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
          如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
          如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配(見2);如果沒有匹配,則回退為一個原始類型(UserDao)進行匹配,如果匹配則自動裝配;


          1.6. @PostConstruct(JSR-250)
          在方法上加上注解@PostConstruct,這個方法就會在Bean初始化之后被Spring容器執行(注:Bean初始化包括,實例化Bean,并裝配Bean的屬性(依賴注入))。
          它的一個典型的應用場景是,當你需要往Bean里注入一個其父類中定義的屬性,而你又無法復寫父類的屬性或屬性的setter方法時,如:
          Java代碼
          public class UserDaoImpl extends HibernateDaoSupport implements UserDao {  
              private SessionFactory mySessionFacotry;  
              @Resource 
              public void setMySessionFacotry(SessionFactory sessionFacotry) {  
                  this.mySessionFacotry = sessionFacotry;  
              }  
              @PostConstruct 
              public void injectSessionFactory() {  
                  super.setSessionFactory(mySessionFacotry);  
              }  
              ...  


          public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
          private SessionFactory mySessionFacotry;
          @Resource
          public void setMySessionFacotry(SessionFactory sessionFacotry) {
          this.mySessionFacotry = sessionFacotry;
          }
          @PostConstruct
          public void injectSessionFactory() {
          super.setSessionFactory(mySessionFacotry);
          }
          ...
          }

          這里通過@PostConstruct,為UserDaoImpl的父類里定義的一個sessionFactory私有屬性,注入了我們自己定義 的sessionFactory(父類的setSessionFactory方法為final,不可復寫),之后我們就可以通過調用 super.getSessionFactory()來訪問該屬性了。

          1.7. @PreDestroy(JSR-250)
          在方法上加上注解@PreDestroy,這個方法就會在Bean初始化之后被Spring容器執行。由于我們當前還沒有需要用到它的場景,這里不不去演示。其用法同@PostConstruct。

          1.8. 使用<context:annotation-config />簡化配置
          Spring2.1添加了一個新的context的Schema命名空間,該命名空間對注釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配 置。我們知道注釋本身是不會做任何事情的,它僅提供元數據信息。要使元數據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。
          AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor就是處理這些注釋元數據的處理器。但是直接在Spring配置文件中定義這些Bean顯得比較笨拙。Spring為我們提供了一種方便的注冊這些BeanPostProcessor的方式,這就是<context:annotation-config />:
          Java代碼
          <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 />  
          </beans> 

          <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 />
          </beans>

          <context:annotationconfig />將隱式地向Spring容 器注冊AutowiredAnnotationBeanPostProcessor、 CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor以及 RequiredAnnotationBeanPostProcessor這4個BeanPostProcessor。

          2. 使用Spring注解完成Bean的定義
          以上我們介紹了通過@Autowired或@Resource來實現在Bean中自動注入的功能,下面我們將介紹如何注解Bean,從而從XML配置文件中完全移除Bean定義的配置。

          2.1. @Component(不推薦使用)、@Repository、@Service、@Controller
          只需要在對應的類上加上一個@Component注解,就將該類定義為一個Bean了:
          Java代碼
          @Component 
          public class UserDaoImpl extends HibernateDaoSupport implements UserDao {  
              ...  


          @Component
          public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
          ...
          }

          使用@Component注解定義的Bean,默認的名稱(id)是小寫開頭的非限定類名。如這里定義的Bean名稱就是userDaoImpl。你也可以指定Bean的名稱:
          @Component("userDao")
          @Component是所有受Spring管理組件的通用形式,Spring還提供了更加細化的注解形式:@Repository、@Service、@Controller,它們分別對應存儲層Bean,業務層Bean,和展示層Bean。目前版本(2.5)中,這些注解與@Component的語義是一樣的,完全通用,在Spring以后的版本中可能會給它們追加更多的語義。所以,我們推薦使用@Repository、@Service、@Controller來替代@Component。

          2.2. 使用<context:component-scan />讓Bean定義注解工作起來
          Java代碼
          <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.kedacom.ksoa" />  
          </beans> 

          <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.kedacom.ksoa" />
          </beans>

          這里,所有通過<bean>元素定義Bean的配置內容已經被移除,僅需要添加一行<context:component-scan />配置就解決所有問題了——Spring XML配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以注釋形式存在罷了)。<context:component-scan />的base-package屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。
          <context:component-scan />還允許定義過濾器將基包下的某些類納入或排除。Spring支持以下4種類型的過濾方式:

          過濾器類型 表達式范例 說明
          注解 org.example.SomeAnnotation 將所有使用SomeAnnotation注解的類過濾出來
          類名指定 org.example.SomeClass 過濾指定的類
          正則表達式 com".kedacom".spring".annotation".web"..* 通過正則表達式過濾一些類
          AspectJ表達式 org.example..*Service+ 通過AspectJ表達式過濾一些類

          以正則表達式為例,我列舉一個應用實例:
          Java代碼
          <context:component-scan base-package="com.casheen.spring.annotation">  
              <context:exclude-filter type="regex" expression="com".casheen".spring".annotation".web"..*" />  
          </context:component-scan> 

          <context:component-scan base-package="com.casheen.spring.annotation">
          <context:exclude-filter type="regex" expression="com".casheen".spring".annotation".web"..*" />
          </context:component-scan>

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

          2.3. 使用@Scope來定義Bean的作用范圍
          在使用XML定義Bean時,我們可能還需要通過bean的scope屬性來定義一個Bean的作用范圍,我們同樣可以通過@Scope注解來完成這項工作:
          Java代碼
          @Scope("session")  
          @Component()  
          public class UserSessionBean implements Serializable {  
              ...  


          @Scope("session")
          @Component()
          public class UserSessionBean implements Serializable {
          ...
          }


          3. 參考
          http://kingtai168.javaeye.com/blog/244002
          http://www.javaeye.com/topic/244153
          http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-annotation-config
          http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-classpath-scanning
          posted on 2010-01-16 23:30 seal 閱讀(4823) 評論(0)  編輯  收藏 所屬分類: Spring
          主站蜘蛛池模板: 孟村| 乐业县| 原阳县| 安远县| 沙洋县| 东山县| 宝鸡市| 怀仁县| 牟定县| 兴仁县| 景泰县| 盱眙县| 阳谷县| 通江县| 花莲县| 台东县| 芒康县| 庄浪县| 济源市| 闸北区| 崇文区| 巍山| 明星| 峨边| 丽江市| 延津县| 黑山县| 绥阳县| 易门县| 威宁| 南阳市| 巫山县| 樟树市| 洱源县| 葵青区| 民乐县| 嘉义市| 墨脱县| 汉沽区| 屯留县| 台东市|