Environment是什么

environment是什么呢....中文是环境大家都知道但是具体代表什么呢?感觉很抽象....从代码里的解释来看environment代表了profile和properties.

profile就是1组bean的定义.实际用途就是在不同环境比如测试环境和生产环境中加载不同的bean达到根据环境加载bean的用途.(因为测试环境可能有些bean是模拟的,比如接口.调用返回的报文都是自己模拟的,真实的bean在测试环境拿不到).

properties就不用说了.就是配置...

这篇文章我想分享下我对properties的学习,因为profile我感觉只要会配置就行了...可能更偏向于配置运维,而properties主要涉及到类型转化等比较复杂的东西.而我自己也写过一些conveter.所以想特别学习下.

结构

大概就是这样的结构.java web环境下一般都是StandardServletEnvironment环境,而我自己做junit测试的时候是StandardEnvironment 这里主要分析我对StandardEnvironment 的学习(子类可能也就增加了一点点其他功能吧.总的来说应该都是大同小异.估计是把servlet的环境变量也加到properties里了.)

environment的profile的功能是定义在Environment类里的

 public interface Environment extends PropertyResolver {

     /**
* Return the set of profiles explicitly made active for this environment. Profiles
* are used for creating logical groupings of bean definitions to be registered
* conditionally, for example based on deployment environment. Profiles can be
* activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
* "spring.profiles.active"} as a system property or by calling
* {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
* <p>If no profiles have explicitly been specified as active, then any {@linkplain
* #getDefaultProfiles() default profiles} will automatically be activated.
* @see #getDefaultProfiles
* @see ConfigurableEnvironment#setActiveProfiles
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
String[] getActiveProfiles(); /**
* Return the set of profiles to be active by default when no active profiles have
* been set explicitly.
* @see #getActiveProfiles
* @see ConfigurableEnvironment#setDefaultProfiles
* @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
*/
String[] getDefaultProfiles(); /**
* Return whether one or more of the given profiles is active or, in the case of no
* explicit active profiles, whether one or more of the given profiles is included in
* the set of default profiles. If a profile begins with '!' the logic is inverted,
* i.e. the method will return true if the given profile is <em>not</em> active.
* For example, <pre class="code">env.acceptsProfiles("p1", "!p2")</pre> will
* return {@code true} if profile 'p1' is active or 'p2' is not active.
* @throws IllegalArgumentException if called with zero arguments
* or if any profile is {@code null}, empty or whitespace-only
* @see #getActiveProfiles
* @see #getDefaultProfiles
*/
boolean acceptsProfiles(String... profiles); }

而properties的功能是定义在PropertyResolver里的

 public interface PropertyResolver {

     /**
* Return whether the given property key is available for resolution, i.e.,
* the value for the given key is not {@code null}.
*/
boolean containsProperty(String key); /**
* Return the property value associated with the given key, or {@code null}
* if the key cannot be resolved.
* @param key the property name to resolve
* @see #getProperty(String, String)
* @see #getProperty(String, Class)
* @see #getRequiredProperty(String)
*/
String getProperty(String key); /**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
* @param key the property name to resolve
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String)
* @see #getProperty(String, Class)
*/
String getProperty(String key, String defaultValue); /**
* Return the property value associated with the given key, or {@code null}
* if the key cannot be resolved.
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @see #getRequiredProperty(String, Class)
*/
<T> T getProperty(String key, Class<T> targetType); /**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String, Class)
*/
<T> T getProperty(String key, Class<T> targetType, T defaultValue); /**
* Convert the property value associated with the given key to a {@code Class}
* of type {@code T} or {@code null} if the key cannot be resolved.
* @throws org.springframework.core.convert.ConversionException if class specified
* by property value cannot be found or loaded or if targetType is not assignable
* from class specified by property value
* @see #getProperty(String, Class)
*/
<T> Class<T> getPropertyAsClass(String key, Class<T> targetType); /**
* Return the property value associated with the given key (never {@code null}).
* @throws IllegalStateException if the key cannot be resolved
* @see #getRequiredProperty(String, Class)
*/
String getRequiredProperty(String key) throws IllegalStateException; /**
* Return the property value associated with the given key, converted to the given
* targetType (never {@code null}).
* @throws IllegalStateException if the given key cannot be resolved
*/
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException; /**
* Resolve ${...} placeholders in the given text, replacing them with corresponding
* property values as resolved by {@link #getProperty}. Unresolvable placeholders with
* no default value are ignored and passed through unchanged.
* @param text the String to resolve
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null}
* @see #resolveRequiredPlaceholders
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
*/
String resolvePlaceholders(String text); /**
* Resolve ${...} placeholders in the given text, replacing them with corresponding
* property values as resolved by {@link #getProperty}. Unresolvable placeholders with
* no default value will cause an IllegalArgumentException to be thrown.
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null}
* or if any placeholders are unresolvable
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; }

让我觉得很奇怪的一个地方就是既然Environment代表了profile+properties,那为什么profile相关方法要写在Environment里,而properties相关方法要写在PropertyResolver里呢...都是@since3.1为什么不再弄个ProfileResolver接口然后Environment接口继承这2个接口呢?

PropertyResolver

从前面的接口代码观察.中我觉得这个类主要就是2个作用:

1.给你1个key你要能找到它对应的value.就是解析properties.

2.在properties的基础上增加了placeholder.value中的一部分可能是占位符,要能根据key找到value同时替换占位符为实际的值.

实验1,一个小测试:

    
ConfigurablePropertyResolver configurablePropertyResolver; // env
 1     /**
* getProperty直接写pro的名字
* resolveRequiredPlaceholders用${}替换pro
*/
@Test
public void testPropertiesResolver() {
System.out.println("a= " + configurablePropertyResolver.getProperty("a"));//a= b
System.out.println("${a}= " + configurablePropertyResolver.getProperty("${a}"));//${a}= null
System.out.println("mmp.a= " + configurablePropertyResolver.getProperty("mmp.a"));//mmp.a= 123
System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("a=${a}"));//a=b
System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("mmp.a=${mmp.a}"));//mmp.a=123
}

加载的配置文件

 a=b
mmp.a=123
email=jyzjyz12@163.com

从这个测试中可以看出

1.加载完properties配置以后我想获得value我就只要简简单单的调用getProperty方法就行了.

2.比如在bean定义的applicationContext.XML里数据源相关的bean可能会使用占位符定义,比如datasource的username和password <property name="username" value="${jdbc.username}" /> 这里的占位符的解析也是通过propertyResolver, resolveRequiredPlaceholders方法或者resolvePlaceholders等相关placeHolder方法来完成.

上面这么多方法其实总结起来就是读取了properties文件,通过key得到value就这么简单...

实验2,再来1个测试:

 ConfigurablePropertyResolver configurablePropertyResolver;

     /**
* PropertyPlaceholderHelper 在进test之前就已经初始化完成了,所以修改这个placeHolderPrefix没用
*/
@Test
public void testConfigurablePropertyResolver() {
configurablePropertyResolver.setPlaceholderPrefix("#{");
configurablePropertyResolver.setPlaceholderSuffix("}");
System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("邮箱地址=${email}")); //可以被替换
System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("邮箱地址=#{email}"));//无效
}

前面测试用到了占位符.默认是${},前缀是${后缀是}......那么我们能不能换个占位符呢? 我们来做1个测试 ↑

这个实验结果似乎是不能..但是接口明明提供了setPlaceholderPrefix和suffix方法为什么会不行呢?

我们稍微跟下断点:

 AbstractEnvironment.java

     private final ConfigurablePropertyResolver propertyResolver =            new PropertySourcesPropertyResolver(this.propertySources);

     @Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}

env里解析placeholder是通过ConfigurablePropertyResolver 来做的.

ConfigurablePropertyResolver 里是用PropertyPlaceholderHelper strictHelper;来做的

     @Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}

然后我再加载spring的XML配置.还没进入junit的测试方法的时候,需要加载指定目录下的bean

<context:component-scan base-package="spring">
</context:component-scan>

这样会导致strictHelper被初始化,ConfigurablePropertyResolver默认的placeholder是${}所以设置到strictHelper里的是${}.

后面进入junit的test方法以后尽管我们去修改了ConfigurablePropertyResolver的placeholder为#{}但是因为strictHelper已经被初始化过了,所以我们并不会重新初始化strictHelper.因此test方法里面修改placeholder为#{}无效.

小实验3:

从实验2种我们已经知道placeholder最终是PropertyPlaceholderHelper来解析的.那么我们是不是可以直接使用它来设置我们自己的placeholder呢?

     /**
* PropertyPlaceholderHelper 替换字符串
*/
@Test
public void testConfigurablePropertyResolver2() {
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("#((", "))");
System.out.println(helper.replacePlaceholders("邮箱地址=#((email))", new PropertyPlaceholderHelper.PlaceholderResolver() { //邮箱地址=jyzjyz12@163.com
@Override
public String resolvePlaceholder(String placeholderName) {
return configurablePropertyResolver.getProperty(placeholderName);
}
}));
}

这里我们新建了1个placeholder是#(())的helper..用它去解析#((email)).....从这个实验中我们大概可以观察到.PropertyPlaceholderHelper 得到#((email))这个字符串以后通过匹配前缀和后缀剥离字符串以后肯定会得到email.然后通过email这个key去environment(或者他的委托类的时候)的properties里去getProperties得到对应的value.

我们来稍微跟一下断点:

当我们调用env的resolveRequiredPlaceholders或者类的其他处理placeholder方法的时候,其实都是通过env内部的PropertyResolver去处理的.就是说env实现了PropertyResolver接口.但是他自己不处理,委托其他类来处理

 AbstractEnvironment.java

     private final ConfigurablePropertyResolver propertyResolver =    new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
PropertySourcesPropertyResolver是用PropertyPlaceholderHelper来处理,这里分为2步,第一步是helper.replacePlaceholders得到剥离了placeholder的key.第二步是通过内部类的resplvePlaceholder方法调用getPropertyAsRawString方法输入key得到value
AbstractPropertyResolver.java

    @Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
    private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}
PropertyPlaceholderHelper来处理的时候会一层一层剥离placeholder,因为placeholder可能有N层.
 /**
* Replaces all placeholders of format {@code ${name}} with the value returned
* from the supplied {@link PlaceholderResolver}.
* @param value the value containing the placeholders to be replaced
* @param placeholderResolver the {@code PlaceholderResolver} to use for replacement
* @return the supplied value with placeholders replaced inline
*/
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, new HashSet<String>());
} protected String parseStringValue(
String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) { StringBuilder result = new StringBuilder(strVal); int startIndex = strVal.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in string value \"" + strVal + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
} return result.toString();
}

29行是个递归调用如果我穿的是${email},29行做完返回的就是email...得到了placeholder中的key以后我们就需要通过内部类PropertyPlaceholderHelper.PlaceholderResolver中的getPropertyAsRawString(placeholderName);去通过key得到value.

这个时候的getPropertyAsRawString(placeholderName);中的placeholderName是email.

这个方法其实就是简单的调用getProperty方法

     @Override
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}

这样就能获得value了.

小结

小结一下environment解决properties和placeholder的方法.

1.如果是properties.直接通过内部PropertySourcesPropertyResolver的getProperty解决

2.如果是placeholder.通过env内部PropertySourcesPropertyResolver相应的resolveRequiredPlaceholders方法(或者其他placeholder方法)来解决.

2.1.这些方法内部会使用helper来解析placeholder....

2.2.PropertyPlaceholderHelper的replacePlaceholders递归调用parseStringValue方法来来剥离placeholder得到key返回给env的PropertySourcesPropertyResolver........

2.3.PropertySourcesPropertyResolver再得到key以后就和查找properties一样了.

所以placeholder相比properties,就是多了一步解析placeholder得到key.利用了PropertyPlaceholderHelper来处理placeholder.

以上便是Environment做为PropertyResolver的用途与原理.

Spring 学习记录2 Environment的更多相关文章

  1. Spring 学习记录3 ConversionService

    ConversionService与Environment的关系 通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和p ...

  2. Spring 学习记录8 初识XmlWebApplicationContext(2)

    主题 接上文Spring 学习记录7 初识XmlWebApplicationContext refresh方法 refresh方法是定义在父类AbstractApplicationContext中的. ...

  3. 我的Spring学习记录(二)

    本篇就简单的说一下Bean的装配和AOP 本篇的项目是在上一篇我的Spring学习记录(一) 中项目的基础上进行开发的 1. 使用setter方法和构造方法装配Bean 1.1 前期准备 使用sett ...

  4. 我的Spring学习记录(四)

    虽然Spring管理这我们的Bean很方便,但是,我们需要使用xml配置大量的Bean信息,告诉Spring我们要干嘛,这还是挺烦的,毕竟当我们的Bean随之增多的话,xml的各种配置会让人很头疼. ...

  5. 我的Spring学习记录(五)

    在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...

  6. Spring 学习记录6 BeanFactory(2)

    主题 除了Spring 学习记录5 BeanFactory 里写的几个接口外,BeanFactory的实现类还实现了一些其他接口,这篇文章主要介绍这些接口和实现类. 结构 DefaultListabl ...

  7. Spring学习记录(九)---通过工厂方法配置bean

    1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. package com.guigu.spring.factory; public class C ...

  8. Spring学习记录(七)---表达式语言-SpEL

    SpEL---Spring Expression Language:是一个支持运行时查询和操作对象图表达式语言.使用#{...}作为定界符,为bean属性动态赋值提供了便利. ①对于普通的赋值,用Sp ...

  9. Spring 学习记录5 BeanFactory

    主题 记录我对BeanFactor接口的简单的学习. BeanFactory我感觉就是管理bean用的容器,持有一堆的bean,你可以get各种bean.然后也提供一些bean相关的功能比如别名呀之类 ...

随机推荐

  1. 20155315 2016-2017-2 《Java程序设计》第八周学习总结

    教材学习内容总结 第14章 NIO与NIO2 1.认识NIO NIO使用频道(Channel)来衔接数据节点,在处理数据时,NIO可以让你设定缓冲区(Buffer)容量,在缓冲区中对感兴趣的数据区块进 ...

  2. CH1809 匹配统计

    题意 描述 阿轩在纸上写了两个字符串,分别记为A和B.利用在数据结构与算法课上学到的知识,他很容易地求出了"字符串A从任意位置开始的后缀子串"与"字符串B"匹配 ...

  3. hasura graphql schema 导出

    使用的是apollo 的插件 安装apollo npm install -g apollo 基本使用 因为我使用了模式拼接,所以地址有变动,一般是 http://host:port/v1alpha1/ ...

  4. 3——FFMPEG之解复用器-----AVInputFormat(转)

    1. 数据结构: AVInputFormat为FFMPEG的解复用器对象,通过调用av_register_all(),FFMPEG所有的解复用器保存在以first_iformat为链表头的链表中,且还 ...

  5. BeagleBoneBlack Linux开发相关链接收藏

    ubuntu挂载vdi文件 官方linux代码地址 官方devicetree代码地址 [转]使用BBB的device tree和cape(重新整理版) iio: input: ti_am335x_ad ...

  6. Bootstrap-CL:分页

    ylbtech-Bootstrap-CL:分页 1.返回顶部 1. Bootstrap 分页 本章将讲解 Bootstrap 支持的分页特性.分页(Pagination),是一种无序列表,Bootst ...

  7. Jenkins构建Python项目提示:'python' 不是内部或外部命令,也不是可运行的程序

    问题描述: jenkin集成python项目,立即构建后,发现未执行成功,查看Console Output 提示:'Python' 不是内部或外部命令,也不是可运行的程序,如下图: 1.在 Windo ...

  8. Express详解

    express() 创建一个express应用程序 var express = require('express'); var app = express(); app.get('/', functi ...

  9. C++ 排序总结

    原帖地址 http://kongec.blog.sohu.com/85141353.html 附  六分钟演示15中算法 http://www.guokr.com/post/482666/ 一.插入排 ...

  10. 深度解析Java中的那把锁

    锁的本质 我们先来讨论锁的出现是为了解决什么问题,锁要保证的事情其实很好理解,同一件事(一个代码块)在同一时刻只能由一个人(线程)操作. 这里所说的锁为排他锁,暂不考虑读写锁的情况 我们在这里打个比方 ...