spring-core env包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