1. 程式人生 > >Spring @Configuration使用

Spring @Configuration使用

雖然 2.0 版本釋出以來,Spring 陸續提供了十多個註解,但是提供的這些註解只是為了在某些情況下簡化 XML 的配置,並非要取代 XML 配置方式。

這一點可以從 Spring IoC 容器的初始化類可以看出:ApplicationContext 介面的最常用的實現類是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及面向 Portlet 的 XmlPortletApplicationContext 和麵向 web 的 XmlWebApplicationContext,它們都是面向 XML 的。

Spring 3.0 新增了另外兩個實現類:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。

從名字便可以看出,它們是為註解而生,直接依賴於註解作為容器配置資訊來 源的 IoC 容器初始化類。

由於 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法與後者相比幾乎沒有什麼差別,因此本文將以 AnnotationConfigApplicationContext 為例進行講解。

AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 註解,自此,XML 配置方式不再是 Spring IoC 容器的唯一配置方式。兩者在一定範圍內存在著競爭的關係,但是它們在大多數情況下還是相互協作的關係,兩者的結合使得 Spring IoC 容器的配置更簡單,更強大。

之前,我們將配置資訊集中寫在 XML 中,如今使用註解,配置資訊的載體由 XML 檔案轉移到了 Java 類中。我們通常將用於存放配置資訊的類的類名以 “Config” 結尾,比如 AppDaoConfig.java、AppServiceConfig.java 等等。

我們需要在用於指定配置資訊的類上加上 @Configuration 註解,以明確指出該類是 Bean 配置的資訊源。並且 Spring 對標註

Configuration 的類有如下要求:
配置類不能是 final 的;配置類不能是本地化的,亦即不能將配置類定義在其他類的方法內部;配置類必須有一個無參建構函式。AnnotationConfigApplicationContext 將配置類中標註了 @Bean 的方法的返回值識別為 Spring Bean,並註冊到容器中,受 IoC 容器管理。

@Bean 的作用等價於 XML 配置中的 標籤。示例如下:

@Configuration  
public class BookStoreDaoConfig{  
@Bean  
public UserDao userDao(){ return new UserDaoImpl();}  
@Bean  
public BookDao bookDao(){return new BookDaoImpl();}  
}  

Spring 在解析到以上檔案時,將識別出標註 @Bean 的所有方法,執行之,並將方法的返回值 ( 這裡是 UserDaoImpl 和 BookDaoImpl 物件 ) 註冊到 IoC 容器中。預設情況下,Bean 的名字即為方法名。因此,與以上配置等價的 XML 配置如下:

@Bean 具有以下四個屬性:
name – 指定一個或者多個 Bean 的名字。這等價於 XML 配置中 的 name 屬性。
initMethod – 容器在初始化完 Bean 之後,會呼叫該屬性指定的方法。這等價於 XML 配置中 的 init-method 屬性。
destroyMethod – 該屬性與 initMethod 功能相似,在容器銷燬 Bean 之前,會呼叫該屬性指定的方法。這等價於 XML 配置中 的 destroy-method 屬性。
autowire – 指定 Bean 屬性的自動裝配策略,取值是 Autowire 型別的三個靜態屬性。Autowire.BY_NAMEAutowire.BY_TYPEAutowire.NO。與 XML 配置中的 autowire 屬性的取值相比,這裡少了 constructor,這是因為 constructor 在這裡已經沒有意義了。

@Bean 沒有直接提供指定作用域的屬性,可以通過 @Scope 來實現該功能,關於 @Scope 的用法已在上文列舉。

基於註解的容器初始化

AnnotationConfigApplicationContext 提供了三個建構函式用於初始化容器。
AnnotationConfigApplicationContext():
該建構函式初始化一個空容器,容器不包含任何 Bean 資訊,需要在稍後通過呼叫其 register() 方法註冊配置類,並呼叫 refresh() 方法重新整理容器。AnnotationConfigApplicationContext(Class…annotatedClasses):
這是最常用的建構函式,通過將涉及到的配置類傳遞給該建構函式,以實現將相應配置類中的 Bean 自動註冊到容器中。AnnotationConfigApplicationContext(String… basePackages):該建構函式會自動掃描以給定的包及其子包下的所有類,並自動識別所有的 Spring Bean,將其註冊到容器中。它不但識別標註 @Configuration 的配置類並正確解析,而且同樣能識別使用 @Repository、@Service、@Controller、@Component 標註的類。

除了使用上面第三種類型的建構函式讓容器自動掃描 Bean 的配置資訊以外,AnnotationConfigApplicationContext 還提供了 scan() 方法,其功能與上面也類似,該方法主要用在容器初始化之後動態增加 Bean 至容器中。呼叫了該方法以後,通常需要立即手動呼叫 refresh() 重新整理容器,以讓變更立即生效。
需要注意的是,AnnotationConfigApplicationContext 在解析配置類時,會將配置類自身註冊為一個 Bean,因為 @Configuration 註解本身定義時被 @Component 標註了。因此可以說,一個 @Configuration 同時也是一個 @Component。

大多數情況下,開發者用不到該 Bean,並且在理想情況下,該 Bean 應該是對開發者透明的。@Configuration 的定義如下所示:

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Configuration {  
String value() default "";  
}  

在一般的專案中,為了結構清晰,通常會根據軟體的模組或者結構定義多個 XML 配置檔案,然後再定義一個入口的配置檔案,該檔案使用 將其他的配置檔案組織起來。最後只需將該檔案傳給 ClassPathXmlApplicationContext 的建構函式即可。

針對基於註解的配置,Spring 也提供了類似的功能,只需定義一個入口配置類,並在該類上使用 @Import 註解引入其他的配置類即可,最後只需要將該入口類傳遞給 AnnotationConfigApplicationContext。具體示例如下:

@Configuration  
@Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class})  
public class BookStoreConfig{ … }  

混合使用 XML 與註解進行 Bean 的配置

設計 @Configuration 和 @Bean 的初衷,並不是為了完全取代 XML,而是為了在 XML 之外多一種可行的選擇。由於 Spring 自發布以來,Spring 開發小組便不斷簡化 XML 配置,使得 XML 配置方式已經非常成熟,加上 Spring 2.0 以後出現了一系列名稱空間的支援,使得 XML 配置方式成為了使用簡單、功能強大的 Bean 定義方式。
而且,XML 配置的一些高階功能目前還沒有相關注解能夠直接支援。因此,在目前的多數專案中,要麼使用純粹的 XML 配置方式進行 Bean 的配置,要麼使用以註解為主,XML 為輔的配置方式進行 Bean 的配置。
之所以會出現兩者共存的情況,主要歸結為三個原因:
其一,目前絕大多數採用 Spring 進行開發的專案,幾乎都是基於 XML 配置方式的,Spring 在引入註解的同時,必須保證註解能夠與 XML 和諧共存,這是前提;
其二,由於註解引入較晚,因此功能也沒有發展多年的 XML 強大,因此,對於複雜的配置,註解還很難獨當一面,在一段時間內仍然需要 XML 的配合才能解決問題。
除此之外,Spring 的 Bean 的配置方式與 Spring 核心模組之間是解耦的,因此,改變配置方式對 Spring 的框架自身是透明的。

Spring 可以通過使用 Bean 後處理器 (BeanPostProcessor) 非常方便的增加對於註解的支援。這在技術實現上非常容易的事情。

要使用混合配置方式,首先需要判斷以哪一種配置方式為主。對這個問題的不同回答將會直接影響到實現的方式。然而大可不必為此傷腦筋,因為不論是以 XML 為主,還是以註解為主,配置方式都是簡單而且容易理解的。這裡不存在錯誤的決定,因為僅僅是表現方式不一樣。
我們首先假設以 XML 配置為主的情況。

對於已經存在的大型專案,可能初期是以 XML 進行 Bean 配置的,後續逐漸加入了註解的支援,這時我們只需在 XML 配置檔案中將被 @Configuration 標註的類定義為普通的 ,同時註冊處理註解的 Bean 後處理器即可。示例如下:

// 假設存在如下的 @Configuration 類:  
package bookstore.config;  
import bookstore.dao.*;  
@Configuration  
public class MyConfig{  
@Bean  
public UserDao userDao(){  
return new UserDaoImpl();  
}  
}  

此時,只需在 XML 中作如下宣告即可:

……

由於啟用了針對註解的 Bean 後處理器,因此在 ApplicationContext 解析到 MyConfig 類時,會發現該類標註了 @Configuration 註解,隨後便會處理該類中標註 @Bean 的方法,將這些方法的返回值註冊為容器總的 Bean。

對於以上的方式,如果存在多個標註了 @Configuration 的類,則需要在 XML 檔案中逐一列出。另一種方式是使用前面提到的自動掃描功能,配置如下:

如此,Spring 將掃描所有 demo.config 包及其子包中的類,識別所有標記了 @Component、@Controller、@Service、@Repository 註解的類,由於 @Configuration 註解本身也用 @Component 標註了,Spring 將能夠識別出 @Configuration 標註類並正確解析之。
對於以註解為中心的配置方式,只需使用 @ImportResource 註解引入存在的 XML 即可,如下所示:

@Configuration  
@ImportResource(“classpath:/bookstore/config/spring-beans.xml”)  
public class MyConfig{  
……  
}  


// 容器的初始化過程和純粹的以配置為中心的方式一致:  
AnnotationConfigApplicationContext ctx =  
new AnnotationConfigApplicationContext(MyConfig.class);  
……