支持不同运行环境下的属性处理--Environment 特性

[size=large]介绍[/size]
Environment架构是spring 3.1版本引入的,它代表当前应用运行环境比如servlet,porlet
。主要处理两部分的内容 profiles和properties,properties将使用org.springframework.core.env.PropertyResolver解析。而Environment继承了此实现。

[size=large]背景[/size]
一个profile是有名字的,在激活状态下它代表注册在容器中的一组bean definitions.一组bean可以通过xml和注解配置被归于到一个profile中,只要这个profile被激活了那这组bean则是可以被注册的。那Environment则担任了访问profile的角色。
Properties 几乎在所有的应用中都担任着重要角色。可能来源于properties file,JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps等等。那Environment则为用户提供了方便的接口以配置property Source和通过它们解析属性。

[size=large]使用EnvironmentAware访问[/size]
可以在ApplicationContext 中为定义一个实现EnvironmentAware接口或@inject来了解这个Environment
在多数情况下,应用中的bean不需要与Enviroment直接交互,相反我们声明诸如PropertySourcesPlaceholderConfigurer的beanFactory处理器(本身实现了EnvironmentAware接口)和<context:property-placeholder/>来解析${}
如果想修改ActiveProfile和Properties则使用ConfigurableEnvironment

[size=large]理解property sources[/size]
Environment架构提供了在一组有层次的property sources上进行迭代查询操作。便于理解,请看下面的例子

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 代表简简单单的键值对。在默认DefaultEnvironment 下,spring配置了两种PropertySource ----一种代表JVM系统属性等价于System.getProperties()返回属性集;另一种代表系统环境变量等价于System.getenv()属性集。就是说如果一个'foo'JVM系统属性或一个'foo'环境变量出现在运行时,那env.containsProperty("foo")调用
将返回true.
而且搜素是具有先后概念的,默认情况下系统属性优先于环境变量。如果'foo'同时出现在JVM系统属性和环境变量中,则系统属性'foo'将胜出。
更重要的是搜素顺序是可以定制的,当想要自定义属性集插入到搜索序列中时可以实现PropertySource 接口并添加到Environment的一组PropertySources中。

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

以上片段中MyPropertySource 被添加到搜索序列中且具有最高优先级。如果它包含'foo',则立即返回不用继续搜索其他的PropertySource 。
MutablePropertySources API提供了更精细化的方法管理一组PropertySource,将在最后介绍

[color=red]注意[/color]:在单独的Application应用中,默认使用DefaultEnvironment;
在web应用中,则使用DefaultWebEnvironment,它在DefaultEnvironment基础上多了servletContext和servletConfig初始化参数
在porlet应用中,则使用DefaultPortletEnvironment,它在DefaultEnvironment基础上 多了porletContext和porletConfig初始化参数;
以上两种可以启用JndiPropertySource
[size=large]使用property sources[/size]
理解property sources基本概念和与Environment的关系之后,如何将这些运用到开发中来,考虑几个场景
场景1:${placeholder}在<import/> 中
假如有几个特定于某些用户的spring配置文件,那么我们可以有条件的载入这些文件通过'customer' 属性值

<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>

spring 3.1之前,import元素中占位符的值只能依赖于JVM system properties 和environment variables,现在就不是这样。Environment架构完全整合到容器中,通过它很容器解决属性占位符路由问题。就是说可以控制搜索过程,比如改变JVM system properties 和environment variables优先级、完全删除它们、添加自己的Property Source。

[color=red]注意[/color]:<import/>元素的处理在BeanFactoryPostProcessors 调用之前,就是说PropertyPlaceholderConfigurer在这无效,因为environment对象和他的一组property source在refresh之前所以在<import/>中的占位符将被正确解析,没有任何生命周期问题。

场景2:${placeholder}在bean definition
多数开发人员熟悉使用PropertyPlaceholderConfigurer 或者 <context:property-placeholder/>替换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,这个组件仍然会查看datasource.properties以解析${database.*},如果没找到则在当前Environment 中查找。当然我们可以改变这种顺序。

[color=red]注意[/color]:在某些情况下,<context:property-placeholder/>依旧会注册PropertyPlaceholderConfigurer,在spring-context schema 3.1版本中,system-properties-mode属性已经移除掉。这是因为这个属性在PropertySources-/Environment世界里不再起作用,但如果使用spring3.1开发,而schema则使用spring-context-3.0.xsd并且设置了system-properties-mode,那<context:property-placeholder>会注册PropertyPlaceholderConfigurer。

<context:property-placeholder system-properties-mode=""/>


[size=large]在web应用中操纵 property sources[/size]
目前为止我们都是通过编程式的访问ApplicationContext操纵property sources
事实上很多spring应用时基于web的,使用ContextLoaderListener管理ApplicationContext。因此引入ApplicationContextInitializer 和它的伙伴servlet context 参数contextInitializerClasses(指定多个,以逗号分隔)
<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时提供了一种方便的方式在容器refresh之前和ApplicationContext交互。正好在这操纵property sources,也可以设置bean.xml setConfigLocations
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值