Spring 系列目录(http://www.javashuo.com/article/p-kqecupyl-bm.html)html
Spring 3.1 提供了新的占位符解析器 PropertyResolver,默认实现为 PropertySourcesPropertyResolver。相关文章以下:java
PropertyResolver 的默认实现是 PropertySourcesPropertyResolver,Environment 实际上也是委托 PropertySourcesPropertyResolver 完成 占位符的解析和类型转换。 类型转换又是委托 ConversionService 完成的。spring
public interface PropertyResolver { // 1. contains boolean containsProperty(String key); // 2.1 获取指定 key,不存在能够指定默认值,也能够抛出异常 String getProperty(String key); String getProperty(String key, String defaultValue); // 2.2 类型转换,委托 ConversionService 完成 <T> T getProperty(String key, Class<T> targetType); <T> T getProperty(String key, Class<T> targetType, T defaultValue); String getRequiredProperty(String key) throws IllegalStateException; <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException; // 3. 解析占位符 ${key} String resolvePlaceholders(String text); String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; }
ConfigurablePropertyResolver 是配置了一些解析占位符的必要属性,如占位符前缀和后缀等ide
public interface ConfigurablePropertyResolver extends PropertyResolver { // 1. 类型转换 ConfigurableConversionService getConversionService(); void setConversionService(ConfigurableConversionService conversionService); // 2.1 ${} 分隔符 void setPlaceholderPrefix(String placeholderPrefix); void setPlaceholderSuffix(String placeholderSuffix); // 2.2 默认属性分隔符 : void setValueSeparator(@Nullable String valueSeparator); // 3.1 ${key} getProperty(key)==null 时是否忽略,不抛出异常 void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders); void setRequiredProperties(String... requiredProperties); void validateRequiredProperties() throws MissingRequiredPropertiesException; }
PropertySourcesPropertyResolver 持有一个数据源 PropertySources,能够经过 getProperty 获取对应的属性值,这方法有几种重载的方法,决定是否解析嵌套占位符和类型转换。源码分析
// 不进行类型转换,但会进行嵌套占位符的解析 @Override public String getProperty(String key) { return getProperty(key, String.class, true); } // 进行类型转换,也进行嵌套占位符的解析 @Override public <T> T getProperty(String key, Class<T> targetValueType) { return getProperty(key, targetValueType, true); } // 不进行类型转换,也不进行嵌套占位符的解析,返回原始的字符串 @Override protected String getPropertyAsRawString(String key) { return getProperty(key, String.class, false); }
咱们再看一下 getProperty(key, String.class, false) 这个方法ui
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { // 1. 从数据源中获取属性值 Object value = propertySource.getProperty(key); if (value != null) { // 2. 若是属性值自己又含有占位符就属于嵌套占位符解析,如 ${a${x}b} if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } // 日志输出 logKeyFound(key, propertySource, value); // 3. 类型转换 return convertValueIfNecessary(value, targetValueType); } } } return null; }
如今最关键的方法是 resolveNestedPlaceholders,用于解析嵌套的占位符,这个方法是在其父类 AbstractPropertyResolver 实现的。this
protected String resolveNestedPlaceholders(String value) { return (this.ignoreUnresolvableNestedPlaceholders ? resolvePlaceholders(value) : resolveRequiredPlaceholders(value)); } private PropertyPlaceholderHelper nonStrictHelper; private PropertyPlaceholderHelper strictHelper; @Override public String resolvePlaceholders(String text) { if (this.nonStrictHelper == null) { this.nonStrictHelper = createPlaceholderHelper(true); } return doResolvePlaceholders(text, this.nonStrictHelper); } @Override public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, this.strictHelper); }
实际上嵌套占位符的解析 PropertySourcesPropertyResolver 都委托给了 PropertyPlaceholderHelper 方法来完成,而自身主要完成从 PropertySources 获取属性值。日志
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) { return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix, this.valueSeparator, ignoreUnresolvablePlaceholders); } private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { return helper.replacePlaceholders(text, this::getPropertyAsRawString); }
在看 PropertyPlaceholderHelper 以前先看一下 PlaceholderResolver 这个内部类,这个类用于获取占位符 key 对应的 value。在 AbstractPropertyResolver#doResolvePlaceholders 方法中将 this::getPropertyAsRawString 传过来了,也就是说 PlaceholderResolver 是从 propertySources 获取对应的 value 值。code
@FunctionalInterface public interface PlaceholderResolver { // 从 propertySource 中获取 placeholderName 的 value String resolvePlaceholder(String placeholderName); }
下面再看 replacePlaceholders 是如何解析嵌套的占位符的。htm
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) { Assert.notNull(value, "'value' must not be null"); // placeholderResolver 是从 propertySources 获取的属性值 return parseStringValue(value, placeholderResolver, new HashSet<>()); } // 循环解析 key ${a${x}b} protected String parseStringValue( String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) { StringBuilder result = new StringBuilder(value); int startIndex = value.indexOf(this.placeholderPrefix); while (startIndex != -1) { // 找到结束的 } 位置,注意嵌套时要找对应的结束标记符 ${a${x}b} int endIndex = findPlaceholderEndIndex(result, startIndex); // endIndex=-1 或 startIndex=-1 结束循环 if (endIndex != -1) { // 1. 获取 ${key} 的 key String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex); String originalPlaceholder = placeholder; // 2. key 出现了循环嵌套,直接 Game Over if (!visitedPlaceholders.add(originalPlaceholder)) { throw new IllegalArgumentException( "Circular placeholder reference '" + originalPlaceholder + "' in property definitions"); } // 2. 循环解析这个 key,若是这个 key 又是形如 ${...} placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); // 3. 至此,这个 key 不可能出现 ${} 了,所以能够放心大胆的从 propertySources 获取对应的 value String propVal = placeholderResolver.resolvePlaceholder(placeholder); // 4. ${key:default} 若是为 null,获取真正的 key,若是为 null 则为默认值 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; } } } // 5. 对不起,value 也可能为 ${...},递归解析 if (propVal != null) { propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal); startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length()); } else if (this.ignoreUnresolvablePlaceholders) { // 6. 忽略没法解析的 key,继续... 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(); }
天天用心记录一点点。内容也许不重要,但习惯很重要!