接續上一篇博客,http://my.oschina.net/guanhe/admin/edit-blog?blog=609241java
咱們繼續:正則表達式
清單 17. 使用 @PostConstruct 和 @PreDestroy 註釋的 Boss.javaspring
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 容器執行了。express
咱們知道,不論是經過實現 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 的時候被觸發執行。post
使用 <context:annotation-config/> 簡化配置測試
Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對註釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。咱們知道註釋自己是不會作任何事情的,它僅提供元數據信息。要使元數據信息真正起做用,必須讓負責處理這些元數據的處理器工做起來。ui
而咱們前面所介紹的 AutowiredAnnotationBeanPostProcessor
和 CommonAnnotationBeanPostProcessor
就是處理這些註釋元數據的處理器。可是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 爲咱們提供了一種方便的註冊這些BeanPostProcessor
的方式,這就是 <context:annotation-config/>。請看下面的配置:spa
清單 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
註釋就能夠達到這個目標了。爲何 @Repository 只能標註在 DAO 類上呢?這是由於該註解的做用不僅是將類識別爲 Bean,同時它還能將所標註的類中拋出的數據訪問異常封裝爲 Spring 的數據訪問異常類型。 Spring 自己提供了一個豐富的而且是與具體的數據訪問技術無關的數據訪問異常結構,用於封裝不一樣的持久層框架拋出的異常,使得異常獨立於底層的框架。
Spring 2.5 在 @Repository 的基礎上增長了功能相似的額外三個註解:@Component、@Service、@Controller,它們分別用於軟件系統的不一樣層次:
@Component 是一個泛化的概念,僅僅表示一個組件 (Bean) ,能夠做用在任何層次。
@Service 一般做用在業務層,可是目前該功能與 @Component 相同。
@Constroller 一般做用在控制層,可是目前該功能與 @Component 相同。
經過在類上使用 @Repository、@Component、@Service 和 @Constroller 註解,Spring 會自動建立相應的 BeanDefinition 對象,並註冊到 ApplicationContext 中。這些類就成了 Spring 受管組件。這三個註解除了做用於不一樣軟件層次的類,其使用方式與 @Repository 是徹底相同的。
下面,咱們徹底使用註釋定義 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
註釋來達到目標,如如下代碼所示:
@scopee
清單 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
對那些比較中立的類進行註釋。
註釋配置和 XML 配置的適用場合
是否有了這些 IOC 註釋,咱們就能夠徹底摒除原來 XML 配置的方式呢?答案是否認的。有如下幾點緣由:
註釋配置不必定在先天上優於 XML 配置。若是 Bean 的依賴關係是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那麼註釋配置優於 XML 配置;反之若是這種依賴關係會在部署時發生調整,XML 配置顯然又優於註釋配置,由於註釋是對 Java 源代碼的調整,您須要從新改寫源代碼並從新編譯才能夠實施調整。
若是 Bean 不是本身編寫的類(如 JdbcTemplate
、SessionFactoryBean
等),註釋配置將沒法實施,此時 XML 配置是惟一可用的方式。
註釋配置每每是類級別的,而 XML 配置則能夠表現得更加靈活。好比相比於 @Transaction
事務註釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。
因此在實現應用中,咱們每每須要同時使用註釋配置和 XML 配置,對於類級別且不會發生變更的配置能夠優先考慮註釋配置;而對於那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 建立和 Bean 注入以前將這兩種配置方式的元信息融合在一塊兒。