StandardEnvironment位于spring-core模块下的org.springframework.core.env目录下,是对外提供Profile,Property管理等接口,下面介绍该目录下核心类的实现,主要是PropertySource和PropertyResolver两个接口体系。
一、PropertySource接口
PropertySource是抽象类,表示一个键值对,注意该类重写了equals()和hashCode()方法,提供了一个named(String name)方法用于构造基于name的PropertySource的空实现,从而便于PropertySource 集合中查找指定属性命的PropertySource。用法如MutablePropertySources中的下列代码:
@Override
@Nullable
public PropertySource<?> get(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.get(index) : null);
}
EnumerablePropertySource也是抽象类,继承PropertySource,增加了String[] getPropertyNames()抽象方法。
CommandLinePropertySource是抽象类,继承自EnumerablePropertySource,增加boolean containsOption(String name),
List<String> getOptionValues(String name),List<String> getNonOptionArgs()三个方法,其中OptionValue指的是命令行中以--开头的参数,NonOptionArgs指的不是以--开头的参数,如--o1=v1 --o2=v2 /path/to/file1 /path/to/file2.
SimpleCommandLinePropertySource和JOptCommandLinePropertySource都是CommandLinePropertySource的实现类,SimpleCommandLinePropertySource是通过SimpleCommandLineArgsParser类解析命令行参数,JOptCommandLinePropertySource是通过OptionParser类解析,依赖JOptSimple这个命令行解析工具包,参考如下测试用例:
@Test
public void withDefaultNonOptionArgsNameAndNonOptionArgsPresent() {
OptionParser parser = new OptionParser();
parser.accepts("o1").withRequiredArg().withValuesSeparatedBy(",");
parser.accepts("o2");
OptionSet optionSet = parser.parse("--o1=v1,v2", "noa1", "--o2", "noa2");
CommandLinePropertySource<?> ps = new JOptCommandLinePropertySource(optionSet);
assertThat(ps.containsProperty("nonOptionArgs"), is(true));
assertThat(ps.containsProperty("o1"), is(true));
assertThat(ps.containsProperty("o2"), is(true));
assertEquals(Arrays.asList("v1","v2"), ps.getOptionValues("o1"));
assertThat(ps.getProperty("o1"), equalTo("v1,v2"));
String nonOptionArgs = (String)ps.getProperty("nonOptionArgs");
assertThat(nonOptionArgs, equalTo("noa1,noa2"));
}
MapPropertySource是EnumerablePropertySource的实现类,Source的类型是Map<String, Object>。PropertiesPropertySource和SystemEnvironmentPropertySource继承自MapPropertySource,PropertiesPropertySource增加了PropertiesPropertySource(String name, Properties source)的构造方法,SystemEnvironmentPropertySource改写了getProperty(String name)方法的实现,从而适配多种环境变量格式,比如获取属性foo.bar的值,会依次尝试获取foo.bar,foo_bar,FOO.BAR,FOO_BAR四个name直到获取值为止。
PropertySources接口继承自Iterable<PropertySource<?>>,定义了多个PropertySource的迭代接口。只有一个默认的实现类MutablePropertySources,通过CopyOnWriteArrayList保存多个PropertySources对象,并增加了维护PropertySource在列表中的具体位置的接口。
重点关注Profiles接口及其关联的解析类ProfilesParser。Profiles的定义如下:
@FunctionalInterface
public interface Profiles {
boolean matches(Predicate<String> activeProfiles);
static Profiles of(String... profiles) {
return ProfilesParser.parse(profiles);
}
}
ProfilesParser的代码解读如下:
final class ProfilesParser {
private ProfilesParser() {
}
static Profiles parse(String... expressions) {
Assert.notEmpty(expressions, "Must specify at least one profile");
Profiles[] parsed = new Profiles[expressions.length];
for (int i = 0; i < expressions.length; i++) {
//把每个表达式解析成对应的Profiles
parsed[i] = parseExpression(expressions[i]);
}
//对多个Profiles做整合
return new ParsedProfiles(expressions, parsed);
}
private static Profiles parseExpression(String expression) {
Assert.hasText(expression, () -> "Invalid profile expression [" + expression + "]: must contain text");
//()&|!为分隔符,true表示分隔符也会被nextToken()方法返回
StringTokenizer tokens = new StringTokenizer(expression, "()&|!", true);
return parseTokens(expression, tokens);
}
private static Profiles parseTokens(String expression, StringTokenizer tokens) {
List<Profiles> elements = new ArrayList<>();
Operator operator = null;
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (token.isEmpty()) {
continue;
}
switch (token) {
case "(":
//此处是递归调用,注意使用的都是一个StringTokenizer对象
elements.add(parseTokens(expression, tokens));
break;
case "&":
//限制operator为null或者AND是为了确保表达式是规范的,A&B|C是不规范的,必
须是(A&B)|C,否则抛异常
assertWellFormed(expression, operator == null || operator ==
Operator.AND);
operator = Operator.AND;
break;
case "|":
assertWellFormed(expression, operator == null || operator ==
Operator.OR);
operator = Operator.OR;
break;
case "!":
//对返回的Profiles执行not操作
elements.add(not(parseTokens(expression, tokens)));
break;
case ")":
//elements保存从(开始的解析的多个Profiles,遇到)则将多个Profiles做合并操
作,同时清空
//elements,将合并后的对象放入进去
Profiles merged = merge(expression, elements, operator);
elements.clear();
elements.add(merged);
operator = null;
break;
default:
//equals方法提供Profiles接口的默认实现
elements.add(equals(token));
}
}
//对保存在elements中的多个Profiles做合并处理
return merge(expression, elements, operator);
}
private static Profiles merge(String expression, List<Profiles> elements, @Nullable Operator operator) {
//效验elements非空
assertWellFormed(expression, !elements.isEmpty());
//如果只有一个元素则返回该元素
if (elements.size() == 1) {
return elements.get(0);
}
Profiles[] profiles = elements.toArray(new Profiles[0]);
//如果有多个则执行且处理或者或处理
return (operator == Operator.AND ? and(profiles) : or(profiles));
}
private static void assertWellFormed(String expression, boolean wellFormed) {
Assert.isTrue(wellFormed, () -> "Malformed profile expression [" + expression + "]");
}
private static Profiles or(Profiles... profiles) {
//满足多个Profiles中的任何一个
return activeProfile -> Arrays.stream(profiles).anyMatch(isMatch(activeProfile));
}
private static Profiles and(Profiles... profiles) {
//满足所有的Profiles
return activeProfile -> Arrays.stream(profiles).allMatch(isMatch(activeProfile));
}
private static Profiles not(Profiles profiles) {
//对原Profiles对象做非处理
return activeProfile -> !profiles.matches(activeProfile);
}
private static Profiles equals(String profile) {
//默认的Profiles接口实现
return activeProfile -> activeProfile.test(profile);
}
private static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {
return profiles -> profiles.matches(activeProfile);
}
private enum Operator {AND, OR}
private static class ParsedProfiles implements Profiles {
private final String[] expressions;
private final Profiles[] parsed;
ParsedProfiles(String[] expressions, Profiles[] parsed) {
this.expressions = expressions;
this.parsed = parsed;
}
@Override
public boolean matches(Predicate<String> activeProfiles) {
for (Profiles candidate : this.parsed) {
//多个Profiles只有有一个满足则返回true
if (candidate.matches(activeProfiles)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return StringUtils.arrayToDelimitedString(this.expressions, " or ");
}
}
}
结合测试用例借助Debug断点调试更容易理解ProfilesParser的实现
@Test
public void ofComplexExpression() {
//最终的效果是Profiles必须是spring 和 framework组合,或者spring 和 java组合
Profiles profiles = Profiles.of("(spring & framework) | (spring & java)");
System.out.println("test spring");
assertFalse(profiles.matches(activeProfiles("spring")));
System.out.println("test spring framework");
assertTrue(profiles.matches(activeProfiles("spring", "framework")));
System.out.println("test spring java");
assertTrue(profiles.matches(activeProfiles("spring", "java")));
System.out.println("test java framework");
assertFalse(profiles.matches(activeProfiles("java", "framework")));
}
private static Predicate<String> activeProfiles(String... profiles) {
return new MockActiveProfiles(profiles);
}
private static class MockActiveProfiles implements Predicate<String> {
private final List<String> activeProfiles;
MockActiveProfiles(String[] activeProfiles) {
this.activeProfiles = Arrays.asList(activeProfiles);
}
@Override
//最终传入test方法的profile的取值依次是spring,frame,spring,java
public boolean test(String profile) {
if (!StringUtils.hasText(profile) || profile.charAt(0) == '!') {
throw new IllegalArgumentException("Invalid profile [" + profile + "]");
}
boolean result=this.activeProfiles.contains(profile);
System.out.println("MockActiveProfiles-->"+profile+",result-->"+result);
return result;
}
}
测试结果如下:
test spring
MockActiveProfiles-->spring,result-->true
MockActiveProfiles-->framework,result-->false //(spring & framework)未匹配
MockActiveProfiles-->spring,result-->true
MockActiveProfiles-->java,result-->false //(spring & java)未匹配
test spring framework
MockActiveProfiles-->spring,result-->true
MockActiveProfiles-->framework,result-->true //(spring & framework)匹配,直接返回
test spring java
MockActiveProfiles-->spring,result-->true
MockActiveProfiles-->framework,result-->false
MockActiveProfiles-->spring,result-->true
MockActiveProfiles-->java,result-->true
test java framework
MockActiveProfiles-->spring,result-->false //(spring & framework)中spring未匹配
MockActiveProfiles-->spring,result-->false //(spring & java)中spring未匹配
二、PropertyResolver接口
PropertyResolver接口定义了按属性名获取对应属性配置的接口以及解析字符串中的属性表达式的接口,如${foo}/abc,foo对应的属性值为123,解析后为123/abc。
Environment接口继承自PropertyResolver,增加了获取Profiles相关接口
ConfigurablePropertyResolver接口继承自PropertyResolver,增加实现PropertyResolver所需的辅助接口
AbstractPropertyResolver是ConfigurablePropertyResolver接口的抽象实现类,提供了大部分接口方法的默认实现,将核心的getProperty(String key, Class<T> targetType)方法留给子类实现,resolvePlaceholders(String text)方法则由PropertyPlaceholderHelper提供默认实现。PropertySourcesPropertyResolver是该类的默认实现类,从全局变量PropertySources对象获取属性名key对应的属性值,查找时会遍历PropertySources中包含的多个PropertySource,直到找到对应的属性值。
ConfigurableEnvironment继承自Environment和ConfigurablePropertyResolver两个接口,并增加了Profiles维护,获取系统属性,系统环境变量的实用接口。
AbstractEnvironment为ConfigurableEnvironment接口的抽象实现类,提供了该接口的所有方法的默认实现,其中ConfigurablePropertyResolver的接口PropertySourcesPropertyResolver实例实现,将核心的MutablePropertySources全局变量包含的多个PropertySources的初始化交给子类实现,即protected方法customizePropertySources(MutablePropertySources propertySources)。
StandardEnvironment是AbstractEnvironment的默认实现子类,覆写了customizePropertySources方法,如下:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
即spring 容器查找环境属性配置时会优先从System Properties中查找,然后是System Env。
重点关注PropertyPlaceholderHelper类的实现,代码解读如下:
public class PropertyPlaceholderHelper {
private static final Log logger = LogFactory.getLog(PropertyPlaceholderHelper.class);
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
static {
wellKnownSimplePrefixes.put("}", "{");
wellKnownSimplePrefixes.put("]", "[");
wellKnownSimplePrefixes.put(")", "(");
}
private final String placeholderPrefix;
private final String placeholderSuffix;
private final String simplePrefix;
@Nullable
private final String valueSeparator;
private final boolean ignoreUnresolvablePlaceholders;
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
this(placeholderPrefix, placeholderSuffix, null, true);
}
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
@Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
this.placeholderPrefix = placeholderPrefix;
this.placeholderSuffix = placeholderSuffix;
String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
this.simplePrefix = simplePrefixForSuffix;
}
else {
this.simplePrefix = this.placeholderPrefix;
}
this.valueSeparator = valueSeparator;
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
public String replacePlaceholders(String value, final Properties properties) {
Assert.notNull(properties, "'properties' must not be null");
return replacePlaceholders(value, properties::getProperty);
}
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, new HashSet<>());
}
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
StringBuilder result = new StringBuilder(value);
//找到表达式前缀在字符串中第一次出现的位置
int startIndex = value.indexOf(this.placeholderPrefix);
//目标字符串中所有的字符
while (startIndex != -1) {
//找到表达式后缀在字符串中与前缀对应的最后一次的位置,需要考虑嵌套的情形
//如ab${cd${ef}}
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
//取出中间的字符串
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
//visitedPlaceholders用于避免递归导致的死循环
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// 递归处理嵌套的属性表达式
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// 找到对应的属性值
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) {
//如果属性值不为空再做递归调用,解析属性值中包含的属性表达式
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
//将属性表达式替换成最终的属性值
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
System.out.println("result-->"+result+",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 value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
//计算表达式前缀后第一个字符的位置
int index = startIndex + this.placeholderPrefix.length();
//记录嵌套的层数
int withinNestedPlaceholder = 0;
//遍历表达式前缀后的每一个字符
while (index < buf.length()) {
//逐个字符比较判断是否是表达式后缀
if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
if (withinNestedPlaceholder > 0) {
//找到嵌套表达式的后缀,将嵌套的层数减1
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
}
else {
//匹配到了后缀且是与前缀对应的一个后缀
return index;
}
}
//逐个字符比较判断是否是表达式前缀
else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
//如果是前缀表明是嵌套
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
}
else {
index++;
}
}
return -1;
}
@FunctionalInterface
public interface PlaceholderResolver {
@Nullable
String resolvePlaceholder(String placeholderName);
}
}
参考测试用例:
@Test
public void testRecurseInPlaceholder() {
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
String text = "foo=${b${inner}}";
Properties props = new Properties();
props.setProperty("bar", "bar");
props.setProperty("inner", "ar");
assertEquals("foo=bar",helper.replacePlaceholders(text, props));
text = "${top}";
props = new Properties();
props.setProperty("top", "${child}+${child}");
props.setProperty("child", "${${differentiator}.grandchild}");
props.setProperty("differentiator", "first");
props.setProperty("first.grandchild", "actualValue");
assertEquals("actualValue+actualValue", helper.replacePlaceholders(text, props));
}
测试用例运行结果:
result-->bar,placeholder-->inner
result-->foo=bar,placeholder-->bar
result-->first.grandchild,placeholder-->differentiator
result-->actualValue,placeholder-->first.grandchild
result-->actualValue+${child},placeholder-->child
result-->first.grandchild,placeholder-->differentiator
result-->actualValue,placeholder-->first.grandchild
result-->actualValue+actualValue,placeholder-->child
result-->actualValue+actualValue,placeholder-->top