Spring 3.1 M1:统一属性管理

工程 | Chris Beams | 2011年2月15日 | ...

在本系列的前面两篇文章中,我介绍了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并将其添加到当前EnvironmentPropertySources集合中即可。


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用户都熟悉使用PropertyPlaceholderConfigurercontext: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.*}占位符,但如果在文件中找不到属性,则会回退到当前EnvironmentPropertySources集合。这再次使您拥有更多控制权;在此更改之前,唯一的回退选项是系统属性和环境变量。

在Web应用程序中操作属性源

到目前为止,我们已经了解了如何在具有对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进行解析。

[3]:在某些情况下,context:property-placeholder/仍然注册PropertyPlaceholderConfigurer。在spring-context模式的3.1版本中,system-properties-mode属性已从property-placeholder元素中删除。这是因为此属性在PropertySources/Environment感知的世界中不再有意义。但是,如果您针对Spring 3.1构建但仍然使用spring-context-3.0.xsd模式并设置system-properties-mode属性,则context:property-placeholder将恢复为注册PropertyPlaceholderConfigurer以便遵守此设置的确切语义。无论如何,这种方法都保留了向后兼容性。

获取Spring新闻通讯

通过Spring新闻通讯保持联系

订阅

领先一步

VMware提供培训和认证,以加快您的进步。

了解更多

获得支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

查看Spring社区中所有即将举行的活动。

查看全部