Spring2.5的Autowired

  1. 使用Spring2.5的Autowired實現註釋型的IOC   
  2. 閱讀(31) 評論(0) 發表時間:20090116日 11:57    
  3. 本文地址:http://qzone.qq.com/blog/55357655-1232078233    
  4.   
  5. 本文標籤: Autowired context IOC xml beans   
  6.     
  7.     
  8.   使用Spring2.5的新特性——Autowired可以實現快速的自動注入,而無需在xml文檔裏面添加bean的聲明,大大減少了xml文檔的維護。(偶喜歡這個功能,因爲偶對xml不感冒)。       以下是一個例子:   
  9. 先編寫接口Man:   
  10.        public interface Man {   
  11.            public String sayHello();   
  12. }   
  13. 然後寫Man的實現類Chinese和American:   
  14.        @Service  
  15. public class Chinese implements Man{   
  16.     public String sayHello() {   
  17.         return "I am Chinese!";   
  18.     }   
  19. }   
  20.   
  21.        @Service  
  22. public class American implements Man{   
  23.     public String sayHello() {   
  24.         return "I am American!";   
  25.     }   
  26. }   
  27.   
  28. @Service註釋表示定義一個bean,自動根據bean的類名實例化一個首寫字母爲小寫的bean,例如Chinese實例化爲chinese,American實例化爲american,如果需要自己改名字則:@Service("你自己改的bean名")。   
  29.   
  30. beans.xml   
  31. <?xml version="1.0" encoding="UTF-8"?>   
  32. <beans xmlns="http://www.springframework.org/schema/beans"  
  33.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  34.         xmlns:context="http://www.springframework.org/schema/context"  
  35.         xsi:schemaLocation="http://www.springframework.org/schema/beans    
  36.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  37.            http://www.springframework.org/schema/context   
  38.            http://www.springframework.org/schema/context/spring-context-2.5.xsd">   
  39.       <context:annotation-config/>   
  40.       <context:component-scan base-package="testspring.main"/>   
  41. </beans>   
  42. 在spring的配置文件裏面只需要加上<context:annotation-config/>和<context:component-scan base-package="需要實現注入的類所在包"/>,可以使用base-package="*"表示全部的類。   
  43.   
  44. 編寫主類測試:   
  45. @Service  
  46. public class Main {   
  47.     @Autowired  
  48.     @Qualifier("chinese")   
  49.     private Man man;   
  50.   
  51.     public static void main(String[] args) {   
  52.         // TODO code application logic here   
  53.         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");   
  54.         Main main = (Main) ctx.getBean("main");   
  55.         System.out.println(main.getMan().sayHello());   
  56.     }   
  57.   
  58.     public Man getMan() {   
  59.         return man;   
  60.     }   
  61. }   
  62.   
  63. 在Man接口前面標上@Autowired@Qualifier註釋使得Man接口可以被容器注入,當Man接口存在兩個實現類的時候必須指定其中一個來注入,使用實現類首字母小寫的字符串來注入。否則可以省略,只寫@Autowired    
  64.   
  65. **********************   
  66. 使用 Spring 2.5 註釋驅動的 IoC 功能   
  67. 發表於08-03-04 20:38 | 閱讀 1285 | 評分 (暫無)    
  68. 概述   
  69.   
  70. 註釋配置相對於 XML 配置具有很多的優勢:   
  71.   
  72. 它可以充分利用 Java 的反射機制獲取類結構信息,這些信息可以有效減少配置的工作。如使用 JPA 註釋配置 ORM 映射時,我們就不需要指定 PO 的屬性名、類型等信息,如果關係表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務屬性映射信息——因爲這些信息都可以通過 Java 反射機制獲取。    
  73. 註釋和 Java 代碼位於一個文件中,而 XML 配置採用獨立的配置文件,大多數配置信息在程序開發完成後都不會調整,如果配置信息和 Java 代碼放在一起,有助於增強程序的內聚性。而採用獨立的 XML 配置文件,程序員在編寫一個功能時,往往需要在程序文件和配置文件中不停切換,這種思維上的不連貫會降低開發效率。    
  74. 因此在很多情況下,註釋配置比 XML 配置更受歡迎,註釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多註釋類,現在您已經可以使用註釋配置完成大部分 XML 配置的功能。在這篇文章裏,我們將向您講述使用註釋進行 Bean 定義和依賴注入的內容。   
  75.   
  76.   
  77.   
  78.     
  79.   
  80.   
  81.  回頁首    
  82.     
  83.   
  84.   
  85.   
  86. 原來我們是怎麼做的   
  87.   
  88. 在使用註釋配置之前,先來回顧一下傳統上是如何配置 Bean 並完成 Bean 之間依賴關係的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置爲 Bean:   
  89.   
  90. Office 僅有一個屬性:   
  91.   
  92.   
  93. 清單 1. Office.java   
  94.                    
  95. package com.baobaotao;   
  96. public class Office {   
  97.     private String officeNo =”001”;   
  98.   
  99.     //省略 get/setter   
  100.   
  101.     @Override  
  102.     public String toString() {   
  103.         return "officeNo:" + officeNo;   
  104.     }   
  105. }   
  106.     
  107.   
  108.   
  109. Car 擁有兩個屬性:   
  110.   
  111.   
  112. 清單 2. Car.java   
  113.                    
  114. package com.baobaotao;   
  115.   
  116. public class Car {   
  117.     private String brand;   
  118.     private double price;   
  119.   
  120.     // 省略 get/setter   
  121.   
  122.     @Override  
  123.     public String toString() {   
  124.         return "brand:" + brand + "," + "price:" + price;   
  125.     }   
  126. }   
  127.     
  128.   
  129.   
  130. Boss 擁有 Office 和 Car 類型的兩個屬性:   
  131.   
  132.   
  133. 清單 3. Boss.java   
  134.                    
  135. package com.baobaotao;   
  136.   
  137. public class Boss {   
  138.     private Car car;   
  139.     private Office office;   
  140.   
  141.     // 省略 get/setter   
  142.   
  143.     @Override  
  144.     public String toString() {   
  145.         return "car:" + car + "\n" + "office:" + office;   
  146.     }   
  147. }   
  148.     
  149.   
  150.   
  151. 我們在 Spring 容器中將 Office 和 Car 聲明爲 Bean,並注入到 Boss Bean 中:下面是使用傳統 XML 完成這個工作的配置文件 beans.xml:   
  152.   
  153.   
  154. 清單 4. beans.xml 將以上三個類配置成 Bean   
  155.                    
  156. <?xml version="1.0" encoding="UTF-8" ?>   
  157. <beans xmlns="http://www.springframework.org/schema/beans"  
  158.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  159.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  160.  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   
  161.     <bean id="boss" class="com.baobaotao.Boss">   
  162.         <property name="car" ref="car"/>   
  163.         <property name="office" ref="office" />   
  164.     </bean>   
  165.     <bean id="office" class="com.baobaotao.Office">   
  166.         <property name="officeNo" value="002"/>   
  167.     </bean>   
  168.     <bean id="car" class="com.baobaotao.Car" scope="singleton">   
  169.         <property name="brand" value=" 紅旗 CA72"/>   
  170.         <property name="price" value="2000"/>   
  171.     </bean>   
  172. </beans>   
  173.     
  174.   
  175.   
  176. 當我們運行以下代碼時,控制檯將正確打出 boss 的信息:   
  177.   
  178.   
  179. 清單 5. 測試類:AnnoIoCTest.java   
  180.                    
  181. import org.springframework.context.ApplicationContext;   
  182. import org.springframework.context.support.ClassPathXmlApplicationContext;   
  183. public class AnnoIoCTest {   
  184.   
  185.     public static void main(String[] args) {   
  186.         String[] locations = {"beans.xml"};   
  187.         ApplicationContext ctx =    
  188.             new ClassPathXmlApplicationContext(locations);   
  189.         Boss boss = (Boss) ctx.getBean("boss");   
  190.         System.out.println(boss);   
  191.     }   
  192. }   
  193.     
  194.   
  195.   
  196. 這說明 Spring 容器已經正確完成了 Bean 創建和裝配的工作。   
  197.   
  198.   
  199.   
  200.     
  201.   
  202.   
  203.  回頁首    
  204.     
  205.   
  206.   
  207.   
  208. 使用 @Autowired 註釋   
  209.   
  210. Spring 2.5 引入了 @Autowired 註釋,它可以對類成員變量、方法及構造函數進行標註,完成自動裝配的工作。來看一下使用 @Autowired 進行成員變量自動注入的代碼:   
  211.   
  212.   
  213. 清單 6. 使用 @Autowired 註釋的 Boss.java   
  214.                    
  215. package com.baobaotao;   
  216. import org.springframework.beans.factory.annotation.Autowired;   
  217.   
  218. public class Boss {   
  219.   
  220.     @Autowired  
  221.     private Car car;   
  222.   
  223.     @Autowired  
  224.     private Office office;   
  225.   
  226.     …   
  227. }   
  228.     
  229.   
  230.   
  231. Spring 通過一個 BeanPostProcessor 對 @Autowired 進行解析,所以要讓 @Autowired 起作用必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。   
  232.   
  233.   
  234. 清單 7. 讓 @Autowired 註釋工作起來   
  235.                    
  236. <?xml version="1.0" encoding="UTF-8" ?>   
  237. <beans xmlns="http://www.springframework.org/schema/beans"  
  238.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  239.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  240.  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">   
  241.   
  242.     <!-- 該 BeanPostProcessor 將自動起作用,對標註 @Autowired 的 Bean 進行自動注入 -->   
  243.     <bean class="org.springframework.beans.factory.annotation.   
  244.         AutowiredAnnotationBeanPostProcessor"/>   
  245.   
  246.     <!-- 移除 boss Bean 的屬性注入配置的信息 -->   
  247.     <bean id="boss" class="com.baobaotao.Boss"/>   
  248.     
  249.     <bean id="office" class="com.baobaotao.Office">   
  250.         <property name="officeNo" value="001"/>   
  251.     </bean>   
  252.     <bean id="car" class="com.baobaotao.Car" scope="singleton">   
  253.         <property name="brand" value=" 紅旗 CA72"/>   
  254.         <property name="price" value="2000"/>   
  255.     </bean>   
  256. </beans>   
  257.     
  258.   
  259.   
  260. 這樣,當 Spring 容器啓動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有 @Autowired 註釋時就找到和其匹配(默認按類型匹配)的 Bean,並注入到對應的地方中去。   
  261.   
  262. 按照上面的配置,Spring 將直接採用 Java 反射機制對 Boss 中的 car 和 office 這兩個私有成員變量進行自動注入。所以對成員變量使用 @Autowired 後,您大可將它們的 setter 方法(setCar() 和 setOffice())從 Boss 中刪除。   
  263.   
  264. 當然,您也可以通過 @Autowired 對方法或構造函數進行標註,來看下面的代碼:   
  265.   
  266.   
  267. 清單 8. 將 @Autowired 註釋標註在 Setter 方法上   
  268.                    
  269. package com.baobaotao;   
  270.   
  271. public class Boss {   
  272.     private Car car;   
  273.     private Office office;   
  274.   
  275.      @Autowired  
  276.     public void setCar(Car car) {   
  277.         this.car = car;   
  278.     }   
  279.     
  280.     @Autowired  
  281.     public void setOffice(Office office) {   
  282.         this.office = office;   
  283.     }   
  284.     …   
  285. }   
  286.     
  287.   
  288.   
  289. 這時,@Autowired 將查找被標註的方法的入參類型的 Bean,並調用方法自動注入這些 Bean。而下面的使用方法則對構造函數進行標註:   
  290.   
  291.   
  292. 清單 9. 將 @Autowired 註釋標註在構造函數上   
  293.                    
  294. package com.baobaotao;   
  295.   
  296. public class Boss {   
  297.     private Car car;   
  298.     private Office office;   
  299.     
  300.     @Autowired  
  301.     public Boss(Car car ,Office office){   
  302.         this.car = car;   
  303.         this.office = office ;   
  304.     }   
  305.     
  306.     …   
  307. }   
  308.     
  309.   
  310.   
  311. 由於 Boss() 構造函數有兩個入參,分別是 car 和 office,@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作爲 Boss(Car car ,Office office) 的入參來創建 Boss Bean。   
  312.   
  313.   
  314.   
  315.     
  316.   
  317.   
  318.  回頁首    
  319.     
  320.   
  321.   
  322.   
  323. 當候選 Bean 數目不爲 1 時的應對方法   
  324.   
  325. 在默認情況下使用 @Autowired 註釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將拋出 BeanCreationException 異常,並指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:   
  326.   
  327.   
  328. 清單 10. 候選 Bean 數目爲 0 時   
  329.                    
  330. <?xml version="1.0" encoding="UTF-8" ?>   
  331. <beans xmlns="http://www.springframework.org/schema/beans"  
  332.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  333.      xsi:schemaLocation="http://www.springframework.org/schema/beans    
  334.  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">   
  335.     
  336.     <bean class="org.springframework.beans.factory.annotation.   
  337.         AutowiredAnnotationBeanPostProcessor"/>    
  338.   
  339.     <bean id="boss" class="com.baobaotao.Boss"/>   
  340.   
  341.     <!-- 將 office Bean 註釋掉 -->   
  342.     <!-- <bean id="office" class="com.baobaotao.Office">   
  343.     <property name="officeNo" value="001"/>   
  344.     </bean>-->   
  345.   
  346.     <bean id="car" class="com.baobaotao.Car" scope="singleton">   
  347.         <property name="brand" value=" 紅旗 CA72"/>   
  348.         <property name="price" value="2000"/>   
  349.     </bean>   
  350. </beans>   
  351.     
  352.   
  353.   
  354. 由於 office Bean 被註釋掉了,所以 Spring 容器中將沒有類型爲 Office 的 Bean 了,而 Boss 的 office 屬性標註了 @Autowired,當啓動 Spring 容器時,異常就產生了。   
  355.   
  356. 當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等於告訴 Spring:在找不到匹配 Bean 時也不報錯。來看一下具體的例子:   
  357.   
  358.   
  359. 清單 11. 使用 @Autowired(required = false)   
  360.                    
  361. package com.baobaotao;   
  362.   
  363. import org.springframework.beans.factory.annotation.Autowired;   
  364. import org.springframework.beans.factory.annotation.Required;   
  365.   
  366. public class Boss {   
  367.   
  368.     private Car car;   
  369.     private Office office;   
  370.   
  371.     @Autowired  
  372.     public void setCar(Car car) {   
  373.         this.car = car;   
  374.     }   
  375.     @Autowired(required = false)   
  376.     public void setOffice(Office office) {   
  377.         this.office = office;   
  378.     }   
  379.     …   
  380. }   
  381.     
  382.   
  383.   
  384. 當然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發期或測試期碰到(如爲了快速啓動 Spring 容器,僅引入一些模塊的 Spring 配置文件),所以 @Autowired(required = false) 會很少用到。   
  385.   
  386. 和找不到一個類型匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啓動時也會拋出 BeanCreationException 異常。來看下面的例子:   
  387.   
  388.   
  389. 清單 12. 在 beans.xml 中配置兩個 Office 類型的 Bean   
  390.                    
  391. …    
  392. <bean id="office" class="com.baobaotao.Office">   
  393.     <property name="officeNo" value="001"/>   
  394. </bean>   
  395. <bean id="office2" class="com.baobaotao.Office">   
  396.     <property name="officeNo" value="001"/>   
  397. </bean>   
  398. …   
  399.     
  400.   
  401.   
  402. 我們在 Spring 容器中配置了兩個類型爲 Office 類型的 Bean,當對 Boss 的 office 成員變量進行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發生了。   
  403.   
  404. Spring 允許我們通過 @Qualifier 註釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:   
  405.   
  406.   
  407. 清單 13. 使用 @Qualifier 註釋指定注入 Bean 的名稱   
  408.                    
  409. @Autowired  
  410. public void setOffice(@Qualifier("office")Office office) {   
  411.     this.office = office;   
  412. }   
  413.     
  414.   
  415.   
  416. @Qualifier("office") 中的 office 是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。@Autowired 可以對成員變量、方法以及構造函數進行註釋,而 @Qualifier 的標註對象是成員變量、方法入參、構造函數入參。正是由於註釋對象的不同,所以 Spring 不將 @Autowired 和 @Qualifier 統一成一個註釋類。下面是對成員變量和構造函數入參進行註釋的代碼:   
  417.   
  418. 對成員變量進行註釋:   
  419.   
  420.   
  421. 清單 14. 對成員變量使用 @Qualifier 註釋   
  422.                    
  423. public class Boss {   
  424.     @Autowired  
  425.     private Car car;   
  426.     
  427.     @Autowired  
  428.     @Qualifier("office")   
  429.     private Office office;   
  430.     …   
  431. }   
  432.     
  433.   
  434.   
  435. 對構造函數入參進行註釋:   
  436.   
  437.   
  438. 清單 15. 對構造函數變量使用 @Qualifier 註釋   
  439.                    
  440. public class Boss {   
  441.     private Car car;   
  442.     private Office office;   
  443.   
  444.     @Autowired  
  445.     public Boss(Car car , @Qualifier("office")Office office){   
  446.         this.car = car;   
  447.         this.office = office ;   
  448.     }   
  449. }   
  450.     
  451.   
  452.   
  453. @Qualifier 只能和 @Autowired 結合使用,是對 @Autowired 有益的補充。一般來講,@Qualifier 對方法簽名中入參進行註釋會降低代碼的可讀性,而對成員變量註釋則相對好一些。   
  454.   
  455.   
  456.   
  457.     
  458.   
  459.   
  460.  回頁首    
  461.     
  462.   
  463.   
  464.   
  465. 使用 JSR-250 的註釋   
  466.   
  467. Spring 不但支持自己定義的 @Autowired 的註釋,還支持幾個由 JSR-250 規範定義的註釋,它們分別是 @Resource@PostConstruct 以及 @PreDestroy。   
  468.   
  469. @Resource  
  470.   
  471. @Resource 的作用相當於 @Autowired,只不過 @Autowired 按 byType 自動注入,面 @Resource 默認按 byName 自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將 @Resource 註釋的 name 屬性解析爲 Bean 的名字,而 type 屬性則解析爲 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。   
  472.   
  473. Resource 註釋類位於 Spring 發佈包的 lib/j2ee/common-annotations.jar 類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用 @Resource 的例子:   
  474.   
  475.   
  476. 清單 16. 使用 @Resource 註釋的 Boss.java   
  477.                    
  478. package com.baobaotao;   
  479.   
  480. import javax.annotation.Resource;   
  481.   
  482. public class Boss {   
  483.     // 自動注入類型爲 Car 的 Bean   
  484.     @Resource  
  485.     private Car car;   
  486.   
  487.     // 自動注入 bean 名稱爲 office 的 Bean   
  488.     @Resource(name = "office")   
  489.     private Office office;   
  490. }   
  491.     
  492.   
  493.   
  494. 一般情況下,我們無需使用類似於 @Resource(type=Car.class) 的註釋方式,因爲 Bean 的類型信息可以通過 Java 反射從代碼中獲取。   
  495.   
  496. 要讓 JSR-250 的註釋生效,除了在 Bean 類中標註這些註釋外,還需要在 Spring 容器中註冊一個負責處理這些註釋的 BeanPostProcessor:   
  497.   
  498. <bean    
  499.   class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>   
  500.     
  501.   
  502.   
  503. CommonAnnotationBeanPostProcessor 實現了 BeanPostProcessor 接口,它負責掃描使用了 JSR-250 註釋的 Bean,並對它們進行相應的操作。   
  504.   
  505. @PostConstruct 和 @PreDestroy  
  506.   
  507. Spring 容器中的 Bean 是有生命週期的,Spring 允許在 Bean 在初始化完成後以及 Bean 銷燬前執行特定的操作,您既可以通過實現 InitializingBean/DisposableBean 接口來定製初始化之後 / 銷燬之前的操作方法,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之後 / 銷燬之前調用的操作方法。關於 Spring 的生命週期,筆者在《精通 Spring 2.x—企業應用開發精解》第 3 章進行了詳細的描述,有興趣的讀者可以查閱。   
  508.   
  509. JSR-250 爲初始化之後/銷燬之前方法的指定定義了兩個註釋類,分別是 @PostConstruct 和 @PreDestroy,這兩個註釋只能應用於方法上。標註了 @PostConstruct 註釋的方法將在類實例化後調用,而標註了 @PreDestroy 的方法將在類銷燬之前調用。   
  510.   
  511.   
  512. 清單 17. 使用 @PostConstruct 和 @PreDestroy 註釋的 Boss.java   
  513.                    
  514. package com.baobaotao;   
  515.   
  516. import javax.annotation.Resource;   
  517. import javax.annotation.PostConstruct;   
  518. import javax.annotation.PreDestroy;   
  519.   
  520. public class Boss {   
  521.     @Resource  
  522.     private Car car;   
  523.   
  524.     @Resource(name = "office")   
  525.     private Office office;   
  526.   
  527.     @PostConstruct  
  528.     public void postConstruct1(){   
  529.         System.out.println("postConstruct1");   
  530.     }   
  531.   
  532.     @PreDestroy  
  533.     public void preDestroy1(){   
  534.         System.out.println("preDestroy1");    
  535.     }   
  536.     …   
  537. }   
  538.     
  539.   
  540.   
  541. 您只需要在方法前標註 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化後或銷燬之前被 Spring 容器執行了。   
  542.   
  543. 我們知道,不管是通過實現 InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的 init-method/destroy-method 屬性進行配置,都只能爲 Bean 指定一個初始化 / 銷燬的方法。但是使用 @PostConstruct 和 @PreDestroy 註釋卻可以指定多個初始化 / 銷燬方法,那些被標註 @PostConstruct 或 @PreDestroy 註釋的方法都會在初始化 / 銷燬時被執行。   
  544.   
  545. 通過以下的測試代碼,您將可以看到 Bean 的初始化 / 銷燬方法是如何被執行的:   
  546.   
  547.   
  548. 清單 18. 測試類代碼   
  549.                    
  550. package com.baobaotao;   
  551.   
  552. import org.springframework.context.support.ClassPathXmlApplicationContext;   
  553.   
  554. public class AnnoIoCTest {   
  555.   
  556.     public static void main(String[] args) {   
  557.         String[] locations = {"beans.xml"};   
  558.         ClassPathXmlApplicationContext ctx =    
  559.             new ClassPathXmlApplicationContext(locations);   
  560.         Boss boss = (Boss) ctx.getBean("boss");   
  561.         System.out.println(boss);   
  562.         ctx.destroy();// 關閉 Spring 容器,以觸發 Bean 銷燬方法的執行   
  563.     }   
  564. }   
  565.     
  566.   
  567.   
  568. 這時,您將看到標註了 @PostConstruct 的 postConstruct1() 方法將在 Spring 容器啓動時,創建 Boss Bean 的時候被觸發執行,而標註了 @PreDestroy 註釋的 preDestroy1() 方法將在 Spring 容器關閉前銷燬 Boss Bean 的時候被觸發執行。   
  569.   
  570.   
  571.   
  572.     
  573.   
  574.   
  575.  回頁首    
  576.     
  577.   
  578.   
  579.   
  580. 使用 <context:annotation-config/> 簡化配置   
  581.   
  582. Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對註釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道註釋本身是不會做任何事情的,它僅提供元數據信息。要使元數據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。    
  583.   
  584. 而我們前面所介紹的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是處理這些註釋元數據的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 爲我們提供了一種方便的註冊這些 BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:   
  585.   
  586.   
  587. 清單 19. 調整 beans.xml 配置文件   
  588.                    
  589. <?xml version="1.0" encoding="UTF-8" ?>   
  590. <beans xmlns="http://www.springframework.org/schema/beans"  
  591.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  592.      xmlns:context="http://www.springframework.org/schema/context"  
  593.      xsi:schemaLocation="http://www.springframework.org/schema/beans    
  594.  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  595.  http://www.springframework.org/schema/context    
  596.  http://www.springframework.org/schema/context/spring-context-2.5.xsd">   
  597.     
  598.     <context:annotation-config/>    
  599.   
  600.     <bean id="boss" class="com.baobaotao.Boss"/>   
  601.     <bean id="office" class="com.baobaotao.Office">   
  602.         <property name="officeNo" value="001"/>   
  603.     </bean>   
  604.     <bean id="car" class="com.baobaotao.Car" scope="singleton">   
  605.         <property name="brand" value=" 紅旗 CA72"/>   
  606.         <property name="price" value="2000"/>   
  607.     </bean>   
  608. </beans>   
  609.     
  610.   
  611.   
  612. <context:annotationconfig/> 將隱式地向 Spring 容器註冊 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。   
  613.   
  614. 在配置文件中使用 context 命名空間之前,必須在 <beans> 元素中聲明 context 命名空間。   
  615.   
  616.   
  617.   
  618.     
  619.   
  620.   
  621.  回頁首    
  622.     
  623.   
  624.   
  625.   
  626. 使用 @Component  
  627.   
  628. 雖然我們可以通過 @Autowired 或 @Resource 在 Bean 類中使用自動注入功能,但是 Bean 還是在 XML 文件中通過 <bean> 進行定義 —— 也就是說,在 XML 配置文件中定義 Bean,通過 @Autowired 或 @Resource 爲 Bean 的成員變量、方法入參或構造函數入參提供自動注入的功能。能否也通過註釋定義 Bean,從 XML 配置文件中完全移除 Bean 定義的配置呢?答案是肯定的,我們通過 Spring 2.5 提供的 @Component 註釋就可以達到這個目標了。   
  629.   
  630. 下面,我們完全使用註釋定義 Bean 並完成 Bean 之間裝配:   
  631.   
  632.   
  633. 清單 20. 使用 @Component 註釋的 Car.java   
  634.                    
  635. package com.baobaotao;   
  636.   
  637. import org.springframework.stereotype.Component;   
  638.   
  639. @Component  
  640. public class Car {   
  641.     …   
  642. }   
  643.     
  644.   
  645.   
  646. 僅需要在類定義處,使用 @Component 註釋就可以將一個類定義了 Spring 容器中的 Bean。下面的代碼將 Office 定義爲一個 Bean:   
  647.   
  648.   
  649. 清單 21. 使用 @Component 註釋的 Office.java   
  650.                    
  651. package com.baobaotao;   
  652.   
  653. import org.springframework.stereotype.Component;   
  654.   
  655. @Component  
  656. public class Office {   
  657.     private String officeNo = "001";   
  658.     …   
  659. }   
  660.     
  661.   
  662.   
  663. 這樣,我們就可以在 Boss 類中通過 @Autowired 注入前面定義的 Car 和 Office Bean 了。   
  664.   
  665.   
  666. 清單 22. 使用 @Component 註釋的 Boss.java   
  667.                    
  668. package com.baobaotao;   
  669.   
  670. import org.springframework.beans.factory.annotation.Autowired;   
  671. import org.springframework.beans.factory.annotation.Required;   
  672. import org.springframework.beans.factory.annotation.Qualifier;   
  673. import org.springframework.stereotype.Component;   
  674.   
  675. @Component("boss")   
  676. public class Boss {   
  677.     @Autowired  
  678.     private Car car;   
  679.   
  680.     @Autowired  
  681.     private Office office;   
  682.     …   
  683. }   
  684.     
  685.   
  686.   
  687. @Component 有一個可選的入參,用於指定 Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義爲“boss”。一般情況下,Bean 都是 singleton 的,需要注入 Bean 的地方僅需要通過 byType 策略就可以自動注入了,所以大可不必指定 Bean 的名稱。   
  688.   
  689. 在使用 @Component 註釋後,Spring 容器必須啓用類掃描機制以啓用註釋驅動 Bean 定義和註釋驅動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進行了擴展,提供了這一功能,請看下面的配置:   
  690.   
  691.   
  692. 清單 23. 簡化版的 beans.xml   
  693.                    
  694. <?xml version="1.0" encoding="UTF-8" ?>   
  695. <beans xmlns="http://www.springframework.org/schema/beans"  
  696.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  697.     xmlns:context="http://www.springframework.org/schema/context"  
  698.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  699.  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  700.  http://www.springframework.org/schema/context    
  701.  http://www.springframework.org/schema/context/spring-context-2.5.xsd">   
  702.     <context:component-scan base-package="com.baobaotao"/>   
  703. </beans>   
  704.     
  705.   
  706.   
  707. 這裏,所有通過 <bean> 元素定義 Bean 的配置內容已經被移除,僅需要添加一行 <context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以註釋形式存在罷了)。<context:component-scan/> 的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。   
  708.   
  709. <context:component-scan/> 還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:   
  710.   
  711.   
  712. 表 1. 掃描過濾方式   
  713. 過濾器類型 說明    
  714. 註釋 假如 com.baobaotao.SomeAnnotation 是一個註釋類,我們可以將使用該註釋的類過濾出來。    
  715. 類名指定 通過全限定類名進行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。    
  716. 正則表達式 通過正則表達式定義過濾的類,如下所示: com\.baobaotao\.Default.*    
  717. AspectJ 表達式 通過 AspectJ 表達式定義過濾的類,如下所示: com. baobaotao..*Service+    
  718.   
  719. 下面是一個簡單的例子:   
  720.   
  721. <context:component-scan base-package="com.baobaotao">   
  722.     <context:include-filter type="regex"    
  723.         expression="com\.baobaotao\.service\..*"/>   
  724.     <context:exclude-filter type="aspectj"    
  725.         expression="com.baobaotao.util..*"/>   
  726. </context:component-scan>   
  727.     
  728.   
  729.   
  730. 值得注意的是 <context:component-scan/> 配置項不但啓用了對類包進行掃描以實施註釋驅動 Bean 定義的功能,同時還啓用了註釋驅動自動注入的功能(即還隱式地在內部註冊了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此當使用 <context:component-scan/> 後,就可以將 <context:annotation-config/> 移除了。   
  731.   
  732. 默認情況下通過 @Component 定義的 Bean 都是 singleton 的,如果需要使用其它作用範圍的 Bean,可以通過 @Scope 註釋來達到目標,如以下代碼所示:   
  733.   
  734.   
  735. 清單 24. 通過 @Scope 指定 Bean 的作用範圍   
  736.                    
  737. package com.baobaotao;   
  738. import org.springframework.context.annotation.Scope;   
  739. …   
  740. @Scope("prototype")   
  741. @Component("boss")   
  742. public class Boss {   
  743.     …   
  744. }   
  745.     
  746.   
  747.   
  748. 這樣,當從 Spring 容器中獲取 boss Bean 時,每次返回的都是新的實例了。   
  749.   
  750.   
  751.   
  752.     
  753.   
  754.   
  755.  回頁首    
  756.     
  757.   
  758.   
  759.   
  760. 採用具有特殊語義的註釋   
  761.   
  762. Spring 2.5 中除了提供 @Component 註釋外,還定義了幾個擁有特殊語義的註釋,它們分別是:@Repository@Service 和 @Controller。在目前的 Spring 版本中,這 3 個註釋和 @Component 是等效的,但是從註釋類的命名上,很容易看出這 3 個註釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個註釋和 @Component 相比沒有什麼新意,但 Spring 將在以後的版本中爲它們添加特殊的功能。所以,如果 Web 應用程序採用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別採用 @Repository@Service 和 @Controller 對分層中的類進行註釋,而用 @Component 對那些比較中立的類進行註釋。   
  763.   
  764.   
  765.   
  766.     
  767.   
  768.   
  769.  回頁首    
  770.     
  771.   
  772.   
  773.   
  774. 註釋配置和 XML 配置的適用場合   
  775.   
  776. 是否有了這些 IOC 註釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點原因:   
  777.   
  778. 註釋配置不一定在先天上優於 XML 配置。如果 Bean 的依賴關係是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那麼註釋配置優於 XML 配置;反之如果這種依賴關係會在部署時發生調整,XML 配置顯然又優於註釋配置,因爲註釋是對 Java 源代碼的調整,您需要重新改寫源代碼並重新編譯纔可以實施調整。    
  779. 如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean 等),註釋配置將無法實施,此時 XML 配置是唯一可用的方式。    
  780. 註釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比於 @Transaction 事務註釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。    
  781. 所以在實現應用中,我們往往需要同時使用註釋配置和 XML 配置,對於類級別且不會發生變動的配置可以優先考慮註釋配置;而對於那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 創建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。   
  782.   
  783.   
  784.   
  785.     
  786.   
  787.   
  788.  回頁首    
  789.     
  790.   
  791.   
  792.   
  793. 小結   
  794.   
  795. Spring 在 2.1 以後對註釋配置提供了強力的支持,註釋配置功能成爲 Spring 2.5 的最大的亮點之一。合理地使用 Spring 2.5 的註釋配置,可以有效減少配置的工作量,提高程序的內聚性。但是這並不意味着傳統 XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數據源、緩存池、持久層操作模板類、事務管理等內容的配置上,XML 配置依然擁有不可替代的地位。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章