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. 7-8 List Leaves(25 分)

    Given a tree, you are supposed to list all the leaves in the order of top down, and left to right. I ...

  2. POJ2891 Strange Way to Express Integers

    题意 Language:Default Strange Way to Express Integers Time Limit: 1000MS Memory Limit: 131072K Total S ...

  3. String.valueof;和String = ""+1;的区别

    关于字符串的+操作,单纯的String  s ="" +11;编译器会看做常量""和常量11的拼接操作,常量计算最快:String.valueOf会调用方法,速 ...

  4. linux环境下maven的安装配置

    1.到官网下载maven,上传到服务器上 https://maven.apache.org/download.cgi 2.将压缩包上传服务器对应路径解压: tar -zxvf apache-maven ...

  5. PHP方便快捷的将二维数组中元素的某一列值抽离出来作为此二维数组内元素的key

    得益于PHP的强大的内置数组函数array_column();array_combine(); 举个小栗子: <?php // 先查询出用户的基本信息 $userArray = [['id' = ...

  6. MySQL建表规范与常见问题 (go)

    一. 表设计 库名.表名.字段名必须使用小写字母,“_”分割. 库名.表名.字段名必须不超过12个字符. 库名.表名.字段名见名知意,建议使用名词而不是动词. 建议使用InnoDB存储引擎. 存储精确 ...

  7. 剪贴板增强---Kawvin增强剪贴板_V2.0

    #Persistent SetWorkingDir,%A_ScriptDir% ;设置工作目录 #MaxThreadsPerHotkey ;最大热键数量 #NoEnv ;#Warn #SingleIn ...

  8. C++ 类外定义

    类内定义与内联函数 像下面这种函数名与函数体都写在类里面的函数形式被称作类内定义,编译器编译的时候会把它默认成内联函数. class Student { public: void setAge(int ...

  9. 简易的RPC调用框架(大神写的)

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. RPC 可基于 HTTP 或 TCP 协议,Web Servi ...

  10. javascript使用bind指定接收者

    var json = { jArray: [], jPush: function (c) { this.jArray.push(c); } } var examp = ["123" ...