領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多在本系列 前兩篇 博文 中,我介紹了 Bean 定義配置檔案功能,以及它如何與 Spring 3.1 M1 中新增的Environment 抽象相關聯。今天我們將探討 Environment 的第二個方面——它如何簡化配置屬性管理的問題。
Spring 的 Environment 抽象在可配置的屬性源層級結構上提供了搜尋操作。為了充分解釋,請考慮以下內容
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsFoo = env.containsProperty("foo");
System.out.println("Does my environment contain the 'foo' property? " + containsFoo);
在上面的程式碼片段中,我們看到了一種詢問 Spring 當前環境中是否定義了“foo”屬性的高階方法。為了回答這個問題,Environment 物件會在一組 PropertySource 物件上執行搜尋。PropertySource 是對鍵值對任何來源的簡單抽象,Spring 的 DefaultEnvironment 配置了兩個 PropertySource 物件——一個代表 JVM 系統屬性集(類似於 System.getProperties()),另一個代表系統環境變數集(類似於 System.getenv())[1]。這意味著,如果在執行時存在“foo”系統屬性或“foo”環境變數,則呼叫 env.containsProperty("foo") 將返回 true。
執行的搜尋是分層的。預設情況下,系統屬性的優先順序高於環境變數,因此如果在呼叫 env.getProperty("foo") 時,“foo”屬性恰好在這兩個地方都已設定,那麼系統屬性值將“獲勝”,並優先於環境變數返回。
最重要的是,整個機制是可配置的。也許您有自定義的屬性來源,您想將其整合到此搜尋中。沒問題——只需實現並例項化您自己的 PropertySource,並將其新增到當前 Environment 的 PropertySources 集中。
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在上面的程式碼中,MyPropertySource 已被新增到搜尋中,並具有最高的優先順序。如果它包含“foo”屬性,它將被檢測到並優先於其他任何 PropertySource 中的“foo”屬性返回。MutablePropertySources API 提供了許多允許精確操作屬性源集的方法。有關詳細資訊,請參閱 Javadoc。
現在您已經瞭解了屬性源及其與 Environment 關係的基礎知識,您可能會想,這一切對您作為 Spring 應用程式的開發者來說有什麼意義呢?讓我們考慮幾個場景,看看它們是如何整合在一起的。
場景 1: 語句中的 ${placeholder} 解析
您有一組 Spring 配置檔案,這些檔案配置了特定於您應用程式的客戶的 Bean,並且您使用包含解析為“customer”屬性值的佔位符的 語句來有條件地載入這些檔案。
<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>
在 Spring 3.1 之前, 元素中的佔位符值只能針對 JVM 系統屬性或環境變數進行解析[2]。現在情況不再是這樣了。由於 Environment 抽象已整合到整個容器中,因此很容易透過它來路由佔位符的解析。這意味著您可以按需配置解析過程:更改搜尋系統屬性和環境變數的優先順序,或者完全刪除它們;根據需要將您自己的屬性源新增到混合中。
場景 2:Bean 定義中的 ${placeholder} 解析
大多數 Spring 使用者都會熟悉使用 PropertyPlaceholderConfigurer 或 context:property-placeholder/ 來替換 Spring Bean 定義中的 ${...} 佔位符。這是一個典型的配置
<context:property-placeholder location="com/bank/config/datasource.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClass" value="${database.driver}"/>
<property name="jdbcUrl" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
從 Spring 3.1 開始,context:property-placeholder/ 不再註冊 PropertyPlaceholderConfigurer,而是註冊 PropertySourcesPlaceholderConfigurer[3]。此元件仍然查詢 datasource.properties 檔案來解析上面的 ${database.*} 佔位符,但如果屬性未在檔案中找到,它將回退到當前 Environment 的 PropertySources 集。這再次為您提供了更多的控制權;在此更改之前,唯一的後備選項是系統屬性和環境變數。
到目前為止,我們已經看到了如何在具有對 ApplicationContext 的程式化訪問的獨立應用程式中訪問和操作屬性源。然而,實際上,許多 Spring 應用程式是 Web 應用程式,其中 ApplicationContext 由 Spring 的 ContextLoaderListener 為您管理。因此,我們引入了 ApplicationContextInitializer 介面及其伴侶 contextInitializerClasses Servlet 上下文引數。看看這個
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.bank.MyInitializer</param-value>
</context-param>
public class MyInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
PropertySource ps = new MyPropertySource();
ctx.getEnvironment().getPropertySources().addFirst(ps);
// perform any other initialization of the context ...
}
}
實現和註冊 ApplicationContextInitializer 提供了一種在應用程式上下文重新整理之前與之互動的簡單方法。這是操作屬性源的理想位置,但您也可以呼叫 setConfigLocations(...) 或任何其他設計為在 refresh() 之前呼叫的方法。
Spring 的 Environment 抽象提供了一個配置配置檔案和屬性的單一位置。如先前帖子所述,配置檔案決定了為給定的部署上下文註冊哪些 Bean 定義;本帖所述的屬性支援提供了一個對任何屬性來源的一致抽象,從而在您的應用程式配置中實現了更靈活的屬性訪問和佔位符解析。
在本系列下一篇文章中,我們將探討 Spring 3.1 如何透過 FeatureSpecification 支援使 100% 基於 Java(即無 XML)的應用程式配置成為現實——這是 Spring 3.0 中引入的 @Configuration 類支援的自然演進。
[1]:這些預設屬性源存在於 DefaultEnvironment 中,供獨立應用程式使用。DefaultWebEnvironment 包含額外的預設屬性源,包括 Servlet 配置和 Servlet 上下文引數。DefaultPortletEnvironment 同樣可以訪問 Portlet 配置和 Portlet 上下文引數作為屬性源。兩者都可以選擇啟用 JndiPropertySource。有關詳細資訊,請參閱 Javadoc。
[2]:因為處理BeanFactoryPostProcessors 之前,這意味著即使 PropertyPlaceholderConfigurer 也無法在此提供幫助。由於 Environment 及其 PropertySources 集在容器重新整理之前配置,因此 元素中的佔位符可以針對 Environment 進行解析,而不會出現任何生命週期問題。Environment 進行解析。
[3]:在某些情況下,context:property-placeholder/ 仍會註冊一個 PropertyPlaceholderConfigurer。在 Spring 3.1 版本的 spring-context 模式中,system-properties-mode 屬性已從 property-placeholder 元素中移除。這是因為在支援 PropertySources/Environment 的世界中,此屬性不再有意義。但是,如果您使用 Spring 3.1 構建但仍使用 spring-context-3.0.xsd 模式並設定了 system-properties-mode 屬性,那麼 context:property-placeholder 將恢復註冊 PropertyPlaceholderConfigurer,以遵循此設定的精確語義。無論如何,這種方法都保留了向後相容性。