1.作用:
@Component------------------------泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。(Component-------成分; 組分; 零件)
@Resource------------------------(資源)
@Autowired-----------------------(自動綁定)
@Repository-----------------------於標注數據訪問組件,即DAO組件(repository-------倉庫; 貯藏室,容器。)
@Service----------------------------用於標注業務層組件(我們通常定義的service層就用這個)
@Controller-------------------------用於標注控制層組件(如struts中的action)
這幾個注解的作用相同:都是為實現所在類(即組件)的bean的轉化,然后可以在容器中調用。然后從名字上的作用就是可以明確各個層次和層次的作用。
首先還是先了解為什么要bean轉化。
雖然下面內容超多,但是看完真的有不少幫助,若是你不是很了解的話。
spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。
在一個稍大的項目中,通常會有上百個組件,如果這些組件采用xml的bean定義來配置,顯然會增加配置文件的體積,查找以及維護起來也不太方便。 Spring2.5為我們引入了組件自動掃描機制,他可以在類路徑底下尋找標注了@Component,@Service,@Controller,@Repository注解的類,並把這些類納入進spring容器中管理。它的作用和在xml文件中使用bean節點配置組件時一樣的。要使用自動掃描機制,我們需要打開以下配置信息:
Java代碼
- <?xml version="1.0" encoding="UTF-8" ?> <beansxmlnsbeansxmlns="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.eric.spring”>
- </beans>
其中base-package為需要掃描的包(含所有子包) @Service用於標注業務層組件,@Controller用於標注控制層組件(如struts中的action),@Repository用於標注數據訪問組件,即DAO組件,而@Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。
@Service public class VentorServiceImpl implements iVentorService{ }
@Repository public class VentorDaoImpl implements iVentorDao {}
getBean的默認名稱是類名(頭字母小寫),如果想自定義,可以@Service(“aaaaa”)這樣來指定,這種bean默認是單例的,如果想改變,可以使用@Service(“beanName”)@Scope(“prototype”)來改變。可以使用以下方式指定初始化方法和銷毀方法(方法名任意):
@PostConstruct public void init() { }
@PreDestroy public void destory() { }
注入方式:
把DAO實現類注入到service實現類中,把service的接口(注意不要是service的實現類)注入到action中,注入時不要new 這個注入的類,因為spring會自動注入,如果手動再new的話會出現錯誤,然后屬性加上@Autowired后不需要getter()和setter()方法,Spring也會自動注入。至於更具體的內容,等對注入的方式更加熟練后會做個完整的例子上來。
注解在spring的配置文件里面只需要加上<context:annotation-config/>和<context:component-scanbase-package="需要實現注入的類所在包"/>,可以使用base-package="*"表示全部的類。
< context:component-scan base-package=”com.eric.spring”>
其中base-package為需要掃描的包(含所有子包)
在接口前面標上@Autowired和@Qualifier注釋使得接口可以被容器注入,當接口存在兩個實現類的時候必須指定其中一個來注入,使用實現類首字母小寫的字符串來注入,如:
@Autowired
@Qualifier("chinese")
private Man man;
否則可以省略,只寫@Autowired 。
Spring中@Autowired注解、@Resource注解的區別
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裝配順序
1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;
@Autowired 與@Resource的區別:
1、 @Autowired與@Resource都可以用來裝配bean.都可以寫在字段上,或寫在setter方法上。
2、@Autowired默認按類型裝配(這個注解是屬業spring的),默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性為false,如:@Autowired(required=false),如果我們想使用名稱裝配可以結合@Qualifier注解進行使用,如下:
a.@Autowired() @Qualifier("baseDao")
b.private BaseDao baseDao;
3、@Resource(這個注解屬於J2EE的),默認安裝名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行安裝名稱查找,如果注解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
a.@Resource(name="baseDao")
b.private BaseDao baseDao;
推薦使用:@Resource注解在字段上,這樣就不用寫setter方法了,並且這個注解是屬於J2EE的,減少了與spring的耦合。這樣代碼看起就比較優雅。
使用Spring2.5的新特性——Autowired可以實現快速的自動注入,而無需在xml文檔里面添加bean的聲明,大大減少了xml文檔的維護(我想到安卓開發是不是有一個xml里面每個頁面都要在這聲明,類似的原理,原來web開發也是這么滴,注解出現后,就不用一個個聲明啦)。(偶喜歡這個功能,因為偶對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 配置具有很多的優勢:
它可以充分利用 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);
- }
- }
使用 @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 時,每次返回的都是新的實例了。
注釋配置和 XML 配置的適用場合
是否有了這些 IOC 注釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點原因:
注釋配置不一定在先天上優於 XML 配置。如果 Bean 的依賴關系是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那么注釋配置優於 XML 配置;反之如果這種依賴關系會在部署時發生調整,XML 配置顯然又優於注釋配置,因為注釋是對 Java 源代碼的調整,您需要重新改寫源代碼並重新編譯才可以實施調整。
如果 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 配置依然擁有不可替代的地位。