领先一步
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
进行解析。
[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
以便遵守此设置的确切语义。无论如何,这种方法都保留了向后兼容性。