Spring-org.springframework.core.env.PropertyResolver

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);
	}
}

 

參考:https://cloud.tencent.com/developer/article/1497681

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章