Spring Java 配置 - 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 提出了與 Annotation-Driven Injection 不同的配置方法,因此我們希望通過完全重命名該類別來明確區分這一點。 AnnotationApplicationContext 將一直處於已棄用狀態,直到 1.0.0.rc1 版本發布,屆時它將被永久刪除。
類型安全改進
雖然上面提到的 JavaConfigApplicationContext 的行為在很大程度上與它的前身相似,但它也引入了類型安全的getBean()方法,這些方法充分利用了泛型。 以下程式碼現在有效(並且從此以後是與 JavaConfig 一起使用的首選方法)

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

看啊,沒有轉型! 也沒有基於字串的查找。 當然,這會引發一個問題,「如果 context 中配置了兩個或多個 OrderService 類型的物件怎麼辦?」 這種情況很容易發生,並且有多種解決方法。 為了簡潔起見,我將讓有興趣的人查看參考文件的消除歧義選項部分。

這些類型安全的 getBean() 方法也已安裝到 ConfigurationSupport 基底類別上,因此可以執行以下操作


@Configuration
public class ApplicationConfig extends ConfigurationSupport {
	public @Bean OrderRepository orderRepository() {
		return new JdbcOrderRepository(this.getBean(DataSource.class));
	}
}
重大文件更新
我們努力使 JavaConfig 的文件達到 Spring 聞名的質量標準。 如上所述,參考文件以HTML以及PDF格式提供。 請注意,此文件也作為常規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 等效的功能,以便在配置期間訪問屬性檔案中的值。 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:修復了 sample web.xml 中的拼字錯誤。]

取得 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部