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 进行解析,而不会出现任何生命周期问题。元素可以在不考虑生命周期问题的情况下针对 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,以遵循此设置的精确语义。无论如何,这种方法都保留了向后兼容性。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

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

查看所有