Spring Java 配置 - M3 版本的新增內容

工程 | Chris Beams | 2008年3月27日 | ...

今天標誌著 Spring Java 配置專案(簡稱 JavaConfig)的第三個里程碑版本釋出。該版本包含大量錯誤修復和新功能——我將在下面重點介紹一些最有趣的變化,但首先讓我快速回顧一下 JavaConfig 是什麼。

如果您有 Spring 的使用經驗,以下 XML 配置片段可能會很熟悉。我們假設正在檢視一個名為 application-config.xml 的檔案


<beans>
	<bean id="orderService" class="com.acme.OrderService"/>
		<constructor-arg ref="orderRepository"/>
	</bean>
	<bean id="orderRepository" class="com.acme.OrderRepository"/>
		<constructor-arg ref="dataSource"/>
	</bean>
</beans>

當然,這個XML配置最終將作為一系列指令,供Spring ApplicationContext 例項化和配置我們的Bean。


ApplicationContext ctx = new ClassPathXmlApplicationContext("application-config.xml");
OrderService orderService = (OrderService) ctx.getBean("orderService");

JavaConfig只是提供了另一種配置Spring IoC容器的機制,這次是純Java,而不是需要XML來完成工作。讓我們將上面的配置移植到JavaConfig。


@Configuration
public class ApplicationConfig {
	public @Bean OrderService orderService() {
		return new OrderService(orderRepository());
	}

	public @Bean OrderRepository orderRepository() {
		return new OrderRepository(dataSource());
	}

	public @Bean DataSource dataSource() {
		// instantiate and return an new DataSource ...
	}
}

與原始XML檔案一樣,這個類僅僅是一系列指令,說明如何構建我們應用程式的各種元件。我們將把這些指令提供給一個專門設計用來讀取和執行基於Java的配置指令的 ApplicationContext 實現。


JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
OrderService orderService = ctx.getBean(OrderService.class);

就是這樣!嗯,差不多。當然JavaConfig還有很多內容,但大部分功能集與Spring的XML配置所提供的功能是1:1的。關於如何使用JavaConfig的完整細節,請參閱 參考文件。如果你是JavaConfig的新手,請務必檢視 快速入門 部分。

無論如何,JavaConfig的好處是很直接的:

  • 它是純Java,所以不需要XML。
  • 你在配置程式碼中獲得了面向物件的全部好處。
  • 它是型別安全的,並且易於重構。
  • 你仍然可以獲得核心Spring IoC容器的全部強大功能。

考慮到這一點,讓我們來看看M3版本中發生了哪些變化。

AnnotationApplicationContext 已棄用
這 hardly算是一個“新功能”,但這個變化很重要,值得一提,因為下面我將討論的大部分內容都圍繞著 JavaConfigApplicationContext,它是 AnnotationApplicationContext 的後繼者。為什麼會做出這個改變? AnnotationApplicationContext 與Spring 2.5的Annotation-Driven Injection機制存在顯著的命名衝突。JavaConfig提供了一種不同於Annotation-Driven Injection的配置方法,所以我們希望透過完全重新命名這個類來明確區分這一點。 AnnotationApplicationContext 將保持棄用狀態,直到1.0.0.rc1版本釋出,屆時將被永久移除。
型別安全性的改進
雖然上面提到的 JavaConfigApplicationContext 的行為很大程度上與其前身相似,但它也引入了型別安全的 getBean() 方法,這些方法充分利用了泛型。下面的程式碼現在可以工作了(並且從現在開始是使用JavaConfig的首選方法):

JavaConfigApplicationContext context = new JavaConfigApplicationContext(AppConfig.class);
OrderService orderService = context.getBean(OrderService.class);

看,不用型別轉換!也沒有基於字串的查詢。當然,這會引出一個問題:“如果上下文中配置了兩個或多個 OrderService 型別的物件怎麼辦?” 這種情況很容易發生,並且有多種方法可以解決。為了在本帖中簡潔起見,我將只引用那些有興趣的人檢視參考文件的 消除歧義選項 部分。

這些型別安全的 getBean() 方法也已新增到 ConfigurationSupport 基類中,使得以下操作成為可能:


@Configuration
public class ApplicationConfig extends ConfigurationSupport {
	public @Bean OrderRepository orderRepository() {
		return new JdbcOrderRepository(this.getBean(DataSource.class));
	}
}
主要的文件更新
我們努力使JavaConfig的文件達到Spring享譽盛名的質量水平。如上鍊接所示,參考文件提供 HTMLPDF 格式。請注意,這些文件也包含在透過SourceForge提供的常規 zip分發版 中。與M3中的所有新增一樣,您對文件的反饋將有助於在專案向前推進到1.0 GA版本時改進它。
在Web層對JavaConfig的支援
在此版本之前,JavaConfig必須透過XML進行“引導”,才能與Spring的 ContextLoaderListenerDispatcherServlet 類結合使用。為了解決這一限制,添加了 JavaConfigWebApplicationContext。只需在您的 web.xml 中將此類指定為 contextClass 引數,即可直接使用您的 @Configuration 類。

<web-app>
    <!-- Configure ContextLoaderListener to use JavaConfigWebApplicationContext
         instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
    </context-param>
    <!-- Configuration locations must consist of one or more comma- or space-delimited
         fully-qualified @Configuration classes -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>example.RootApplicationConfig</param-value>
    </context-param>
    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use JavaConfigWebApplicationContext
             instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.config.java.context.JavaConfigWebApplicationContext</param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
             and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>example.web.WebBeansConfig</param-value>
        </init-param>
    </servlet>
</web-app>

在實踐中,許多人可能會希望繼續使用XML和JavaConfig的組合,尤其是在Web應用程式中。這種方法仍然有效(請參閱 組合配置方法),但對團隊來說,重要的是,如果使用者希望完全“無XML”的方法,我們必須提供這種選擇。這一改變完善了這種可能性。

透過新的 @Import 註解實現的模組化改進
就像Spring XML配置的 <import/> 元素一樣,現在可以將一個 @Configuration 類匯入另一個(從而匯入其所有Bean定義)。

@Configuration
public class FooConfig {
	public @Bean Foo foo() { ... }
	public @Bean Bar bar() { ... }
}

@Import(FooConfig.class)
@Configuration
public class ApplicationConfig {
	public @Bean ServiceA serviceA() { ... }
}

JavaConfigApplicationContext ctx = new JavaConfigApplicationContext(ApplicationConfig.class);
// foo, bar, and serviceA beans will all be available
ctx.getBean(ServiceA.class); // works
ctx.getBean(Foo.class); // works too

這項功能只是提供了另一個工具來有效地模組化 @Configuration 類。

使用 @ExternalValue@ResourceBundles 外部化值
有許多人建議在JavaConfig中引入等同於 PropertyPlaceholderConfigurer 功能的功能,以便在配置期間訪問屬性檔案中的值。M3提供了這一點。假設我們有一個典型的 DataSource,它需要JDBC URL、使用者名稱和密碼。和往常一樣,這些值儲存在屬性檔案中。對於下面的示例,該屬性檔案將在我們的類路徑中,路徑為 com/acme/datasource.properties。該檔案的內容如下:
datasource.url=jdbc:localhost:...
datasource.username=scott
datasource.password=tiger

使用 @ResourceBundles@ExternalValue,我們現在可以從JavaConfig內部訪問這些屬性。


@Configuration
@ResourceBundles("classpath:/com/acme/datasource")
public abstract class ApplicationConfig {
	public @Bean OrderService orderService() {
		return new OrderServiceImpl(orderRepository());
	}

	public @Bean OrderRepository orderRepository() {
		return new JdbcOrderRepository(dataSource());
	}

	public @Bean DataSource dataSource() {
		return new DriverManagerDataSource(url(), username(), password());
	}

	abstract @ExternalValue("datasource.url") String url();
	abstract @ExternalValue("datasource.username") String username();
	abstract @ExternalValue("datasource.password") String password();
}

這裡有幾點需要注意:看看 @ResourceBundles 註解的值為什麼不以 .properties 結尾?這是因為JavaConfig底層使用了Spring的國際化基礎設施,並會根據當前區域設定查詢 datasource.properties 的變體,例如 datasource_en.properties。另外,雖然本例將字串值提供給了 @ExternalValue 註解,但預設是根據方法名查詢屬性。因此,如果我們沒有提供 @ExternalValue("datasource.url") String url(),而是隻寫 @ExternalValue String url(),JavaConfig就會查詢名為“url”的屬性。

下一步是什麼?
許多使用者一直在問何時能看到JavaConfig的1.0版本釋出,這很有道理——它已經等待了很長時間!在我們準備將其稱為“生產級”軟體之前,仍有許多重要的更改需要解決,包括內部以及公共API。預計在接下來的幾周內將會有更頻繁的里程碑和候選版本釋出。總而言之:JavaConfig現在並將在未來繼續得到完全支援。如果您想關注進展,請訪問JavaConfig的 JIRA問題跟蹤,特別是 路線圖 檢視。
徵求反饋!
如果您已經讀到這裡,那麼可以肯定您至少對JavaConfig很感興趣 :) 那麼就採取下一步行動吧! 下載釋出版閱讀文件,試用一下,然後 告訴我們您的想法

[更新 3/27:帖子意外刪除,故重新發布——對於已經寫過評論但被刪除的人表示歉意。] [更新 4/4:修正了示例web.xml中的一個拼寫錯誤。]

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有