Spring Java Configuration - M3 中的新特性

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

今天標誌著 Spring Java Configuration 專案(簡稱 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 已棄用
這很難算是一個“新特性”,但提及這個變化很重要,因為我下面討論的很多內容都圍繞著 JavaConfigApplicationContext,它是 AnnotationApplicationContext 的後繼者。為什麼進行這項更改? AnnotationApplicationContext 與 Spring 2.5 的註解驅動注入(Annotation-Driven Injection)功能存在嚴重的命名衝突。JavaConfig 提供了與註解驅動注入不同的配置方法,因此我們希望透過完全重新命名該類來明確區分。 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 格式。請注意,這些文件也作為常規 zip 發行版的一部分打包,可透過 SourceForge 獲取。與 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 的功能,以便在配置期間訪問 properties 檔案中的值。M3 正提供了這一點。假設我們有一個典型的 DataSource 需要其 JDBC URL、使用者名稱和密碼。像往常一樣,這些值儲存在 properties 檔案中。對於下面的示例,該 properties 檔案將在我們的類路徑中可用,路徑為 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 社群所有即將舉行的活動。

檢視全部