PropertyResolver
該接口用於解析Spring項目中一系列的基礎屬性。例如properties文件,yaml文件,甚至是一些nosql(因爲nosql也是k-v形式)。
接口中定義了一系列讀取,解析,判斷是否包含指定屬性的方法:
/**
* 判斷能否解析(包含)給定的屬性
*/
boolean containsProperty(String key);
/**
* 返回指定屬性的值
*/
@Nullable
String getProperty(String key);
/**
* 返回執行屬性的值,如果該屬性不存在或值不存在,返回給定的默認值
*/
String getProperty(String key, String defaultValue);
/**
* 返回指定屬性且指定類型的值
*/
@Nullable
<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;
/**
* 解析文本中${...}屬性的值,並做替換,然後返回替換後的文本
*/
String resolvePlaceholders(String text);
/**
* 解析文本中${...}屬性的值,並做替換,然後返回替換後的文本,${...}中的屬性必須存在否則拋出異常
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
繼承鏈如下:PropertyResolver -> ConfigurablePropertyResolver -> AbstractPropertyResolver -> PropertySourcesPropertyResolver,Environment也是它的分支。
ConfigurablePropertyResolver
/**
*
*/ 獲取轉換器
ConfigurableConversionService getConversionService()
/**
* 設置轉換器
*/
void setConversionService(ConfigurableConversionService conversionService);
/**
* 設置佔位符前綴
*/
void setPlaceholderPrefix(String placeholderPrefix);
/**
* 設置佔位符後綴
*/
void setPlaceholderSuffix(String placeholderSuffix);
/**
* 設置分隔符
*/
void setValueSeparator(@Nullable String valueSeparator);
/**
* 設置是否忽略無法解析的佔位符,默認爲false不忽略,無法解析會拋出異常
*/
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
/*
* 設置必須的屬性集合
*/
void setRequiredProperties(String... requiredProperties);
/**
* 驗證必須的屬性集合是否存在,如果不存在會拋出異常
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;
ConfigurableXXX
成了Spring的一種命名規範,或者說是一種設計模式。它表示可配置的,所以都會提供大量的set方法。
Spring很多接口都是讀寫分離的,最頂層接口一般都只會提供只讀方法,這是Spring框架設計的一般規律之一。
AbstractPropertyResolver
它是對ConfigurablePropertyResolver
的一個抽象實現,實現了除
/** * 底層的獲取屬性值的方法,具體實現爲從某類容器中獲取值,比如PropertySourcesPropertyResolver中是從PropertySources中獲取的 */ <T> T getProperty(String key, Class<T> targetType)
方法外的所有的接口方法,並且只提供一個抽象方法給子類去實現~~~
/**
* 轉換器,使用volatile修飾
*/
@Nullable
private volatile ConfigurableConversionService conversionService;
/**
* 嚴格的佔位符操作類,即不忽略無法解析的佔位符,會拋出異常
*/
@Nullable
private PropertyPlaceholderHelper nonStrictHelper;
/**
* 非嚴格的佔位符操作類,即忽略無法解析的佔位符,不會拋出異常
*/
@Nullable
private PropertyPlaceholderHelper strictHelper;
/**
* 是否忽略無法解析的佔位符,默認爲false不忽略,如果無法解析會拋出異常
*/
private boolean ignoreUnresolvableNestedPlaceholders = false;
/**
* 佔位符前綴,默認爲${
*/
private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
/**
* 佔位符後綴,默認爲}
*/
private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
/**
* 值的分隔符,默認爲:
*/
@Nullable
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
/**
* 必須的屬性集合
*/
private final Set<String> requiredProperties = new LinkedHashSet<>();
/**
* 獲取轉換器
*/
@Override
public ConfigurableConversionService getConversionService() {
// Need to provide an independent DefaultConversionService, not the
// shared DefaultConversionService used by PropertySourcesPropertyResolver.
ConfigurableConversionService cs = this.conversionService;
if (cs == null) {
synchronized (this) {
cs = this.conversionService;
if (cs == null) {
cs = new DefaultConversionService();
this.conversionService = cs;
}
}
}
return cs;
}
// ...... 中間一些基本get/set方法忽略
/**
* 解析佔位符,注意這裏使用的是非嚴格解析
*/
@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);
}
protected String resolveNestedPlaceholders(String value) {
return (this.ignoreUnresolvableNestedPlaceholders ?
resolvePlaceholders(value) : resolveRequiredPlaceholders(value));
}
/**
* 創建PropertyPlaceholderHelper
*/
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
/**
* 通過PropertyPlaceholderHelper解析佔位符
*/
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
/**
* 轉換值
*/
@SuppressWarnings("unchecked")
@Nullable
protected <T> T convertValueIfNecessary(Object value, @Nullable Class<T> targetType) {
if (targetType == null) {
return (T) value;
}
ConversionService conversionServiceToUse = this.conversionService;
if (conversionServiceToUse == null) {
// Avoid initialization of shared DefaultConversionService if
// no standard type conversion is needed in the first place...
if (ClassUtils.isAssignableValue(targetType, value)) {
return (T) value;
}
conversionServiceToUse = DefaultConversionService.getSharedInstance();
}
return conversionServiceToUse.convert(value, targetType);
}
/**
*
*/
@Nullable
protected abstract String getPropertyAsRawString(String key);
最終,解析佔位符的核心邏輯在PropertyPlaceholderHelper
身上,這個類不可小覷,是一個與業務無關非常強大的工具類,我們可以直接拿來主義~
PropertyPlaceholderHelper
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) {
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");
}
// 使用遞歸調用解析嵌套佔位符,比如${user_${age}}
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 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) {
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 {
/**
* Resolve the supplied placeholder name to the replacement value.
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or {@code null} if no replacement is to be made
*/
@Nullable
String resolvePlaceholder(String placeholderName);
}
PropertySourcesPropertyResolver
從上面知道AbstractPropertyResolver
封裝瞭解析佔位符的具體實現。PropertySourcesPropertyResolver
作爲它的子類它只需要提供數據源,所以它主要是負責提供數據源。
// propertySources就是上面提到的數據源,所有的屬性值放在這個裏面
@Nullable
private final PropertySources propertySources;
public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
this.propertySources = propertySources;
}
@Override
public boolean containsProperty(String key) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (propertySource.containsProperty(key)) {
return true;
}
}
}
return false;
}
//...... get/set方法省略
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
return convertValueIfNecessary(value, targetValueType);
}
}
}
return null;
}
PropertySources
是spirng管理和保存屬性配置的關鍵接口
// 接口,抽象了屬性的基本操作接口
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* 獲取屬性配置流
*/
default Stream<PropertySource<?>> stream() {
return StreamSupport.stream(spliterator(), false);
}
/**
* 是否包含
*/
boolean contains(String name);
/**
* 獲取
*/
@Nullable
PropertySource<?> get(String name);
}
// 屬性名稱和屬性對象的抽象類
public abstract class PropertySource<T> {
// 屬性名稱
protected final String name;
// 屬性名稱對應的值
protected final T source;
/**
* 構造方法
*/
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
/**
* 構造方法
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
public String getName() {
return this.name;
}
public T getSource() {
return this.source;
}
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
// 根據name獲取source,抽象方法
@Nullable
public abstract Object getProperty(String name);
/**
* 生成一個僅根據name比較的屬性源對象,不可用獲取name和source
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
/**
* 內部實現類, 存根屬性源類,主要用在測試場景
* @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
* @see org.springframework.web.context.support.StandardServletEnvironment
* @see org.springframework.web.context.support.ServletContextPropertySource
*/
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
@Override
@Nullable
public String getProperty(String name) {
return null;
}
}
/**
* 內部實現類,可比較的屬性源類,僅用作根據名稱進行比較的場景
*/
static class ComparisonPropertySource extends StubPropertySource {
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
@Nullable
public String getProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
}
PropertySource實現類有很多,這裏選取PropertiesPropertySource分析下
PropertiesPropertySource
PropertiesPropertySource的繼承關係如圖:
EnumerablePropertySource
/**
* 抽象類
*/
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
protected static final String[] EMPTY_NAMES_ARRAY = new String[0];
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
public abstract String[] getPropertyNames();
public boolean containsProperty(String name) {
Assert.notNull(name, "Property name must not be null");
for (String candidate : getPropertyNames()) {
if (candidate.equals(name)) {
return true;
}
}
return false;
}
}
MapPropertySource
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
// 注意屬性對象是Map
public MapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public Object getProperty(String name) {
return this.source.get(name);
}
@Override
public String[] getPropertyNames() {
return this.source.keySet().toArray(EMPTY_NAMES_ARRAY);
}
}
PropertiesPropertySource
public class PropertiesPropertySource extends MapPropertySource {
@SuppressWarnings({ "unchecked", "rawtypes" })
public PropertiesPropertySource(String name, Properties source) {
super(name, (Map) source);
}
}