介绍
PropertySource是spring中对于键值属性的一种抽象,主要是name和source。source是任意对象,可以是对象,可以是Map,可以是List等等。但是一般常用的却不是source,而是抽象的方法getProperty。根据name,获取source中的值,也就是说由继承类来实现getProperty然后返回数据。例如如果souce是Map,那就从map中获取value值,如果Source是List,那可能就是找到遍历找到值,如果是对象就直接get获得值等等。接下来看下源码。
源码
PropertySource
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
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 abstract Object getProperty(String name);
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof PropertySource &&
ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
/**
* Always returns {@code null}.
*/
@Override
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
public String getProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
}
在PropertySource中有两个内部类需要了解下,一个是StubPropertySource还有一个是ComparisonPropertySource;这两个内部类有什么作用呢?
StubPropertySource:其实一般PropertySource在使用的时候都是使用PropertySource的集合,所以在集合中就会有先后顺序的作用,这个StubPropertySource就是用作占位用的。
ComparisonPropertySource:既然PropertySource一般用作集合中,那如果要查找某个名称的PropertySource的位置(indexOf),那必须要是PropertySource才行,所以这时候就 new ComparisonPropertySource(name),就像named那个方法一样。 仅仅是用作在集合中的比较而已,通过源码中看到重写的equals和hashCode都是对于名称来计算的。
PropertyResolver
但是由于继承PropertySource的类很多,为了统一封装对外出口,所以spring就提供了一个PropertyResolver接口,实现PropertyResolver统一获取PropertySource的信息。
PropertyResolver是对于PropertySource对外提供数据的统一封装,并且其中还提供了类型转换的功能。由spring内部提供ConversionService对取得的数据进行相应的转换,转换成期望的数据类型。
接下来看下PropertyResolver的接口和具体实现
public interface PropertyResolver {
boolean containsProperty(String key);
// 获取数据
String getProperty(String key);
// 获取数据,如果是空,默认使用defaultValue
String getProperty(String key, String defaultValue);
// 转化成指定类型的数据
<T> T getProperty(String key, Class<T> targetType);
// 转化成指定类型的数据,如果是空,默认使用defaultValue
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
@Deprecated
<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
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;
}
PropertySourcesPropertyResolver
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
@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;
}
@Override
@Nullable
public String getProperty(String key) {
return getProperty(key, String.class, true);
}
@Override
@Nullable
public <T> T getProperty(String key, Class<T> targetValueType) {
return getProperty(key, targetValueType, true);
}
// 回调会用到
@Override
@Nullable
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
// 遍历PropertySources获取PropertySource
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
// 从PropertySource中获取值
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
// 如果是允许解析占位符,解析占位符
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
// 转化成期望的类型
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Could not find key '" + key + "' in any property source");
}
return null;
}
protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) {
if (logger.isDebugEnabled()) {
logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() +
"' with value of type " + value.getClass().getSimpleName());
}
}
}
在PropertySourcesPropertyResolver主要是处理PropertySources,也就是PropertySource的集合,其实主要的就是getProperty的方法,从所有的PropertySource中获取资源,如果获取到了,并且允许解析占位符且值是String型,那就去解析占位符的值。解析了占位符之后,转化成期望的数据类型。 可以看到这边取到第一个满足条件的数据之后就会返回,这也是有些数据排在前面,然后优先的策略。比如命令行参数就比配置文件中参数优先。这些后面也会说到。
对于占位符的解析和数据类型的转化在在AbstractPropertyResolver方法中,继续看AbstractPropertyResolver方法。
package org.springframework.core.env;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils;
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private volatile ConfigurableConversionService conversionService;
@Nullable
private PropertyPlaceholderHelper nonStrictHelper;
@Nullable
private PropertyPlaceholderHelper strictHelper;
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;
}
@Override
public void setConversionService(ConfigurableConversionService conversionService) {
Assert.notNull(conversionService, "ConversionService must not be null");
this.conversionService = conversionService;
}
@Override
public void setPlaceholderPrefix(String placeholderPrefix) {
Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
this.placeholderPrefix = placeholderPrefix;
}
@Override
public void setPlaceholderSuffix(String placeholderSuffix) {
Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
this.placeholderSuffix = placeholderSuffix;
}
@Override
public void setValueSeparator(@Nullable String valueSeparator) {
this.valueSeparator = valueSeparator;
}
@Override
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
this.ignoreUnresolvableNestedPlaceholders = ignoreUnresolvableNestedPlaceholders;
}
@Override
public void setRequiredProperties(String... requiredProperties) {
for (String key : requiredProperties) {
this.requiredProperties.add(key);
}
}
@Override
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
@Override
public boolean containsProperty(String key) {
return (getProperty(key) != null);
}
@Override
@Nullable
public String getProperty(String key) {
return getProperty(key, String.class);
}
@Override
public String getProperty(String key, String defaultValue) {
String value = getProperty(key);
return (value != null ? value : defaultValue);
}
@Override
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
T value = getProperty(key, targetType);
return (value != null ? value : defaultValue);
}
@Override
public String getRequiredProperty(String key) throws IllegalStateException {
String value = getProperty(key);
if (value == null) {
throw new IllegalStateException("Required key '" + key + "' not found");
}
return value;
}
@Override
public <T> T getRequiredProperty(String key, Class<T> valueType) throws IllegalStateException {
T value = getProperty(key, valueType);
if (value == null) {
throw new IllegalStateException("Required key '" + key + "' not found");
}
return value;
}
@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));
}
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);
}
@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);
}
上面AbstractPropertyResolver这么大一段,我们先看对于占位符的解析一块,resolveNestedPlaceholders方法中ignoreUnresolvableNestedPlaceholders默认是false,所以默认占位符是不允许为空的。所以调用resolveRequiredPlaceholders方法。
对于占位符的解析,AbstractPropertyResolver主要是调用PropertyPlaceholderHelper的通用处理方法对于占位符解析。这边再来看看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) {
// 查询占位符的后置位置
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
// 去除包装的字符,得到真正占位符
//1 比如${abc}得到 abc
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");
}
//2 递归调用
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
//3 获取配置中的值
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);
//4 获取分隔符后的默认值
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);
// 将要解析的值把占位符后的值替换成默认值
//5 如 ${abc} 取到的值是123 那么 ${abc}被替换成123
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) {
// 占位符的值允许为空,继续向后查找
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 {
@Nullable
String resolvePlaceholder(String placeholderName);
}
}
PropertyPlaceholderHelper对于占位符的解析简单说下,就是查找有没有被占位符,一般占位符需要有前后包装才可以,比如${abc}这样的。
1.去掉占位符的包装之后,得到内部的占位符,但是可能里面还嵌套占位符。
2.递归调用,直到拿到没有占位符为止。
3.根据占位符的值去配置中获取
4如果获取不到,占位符中有分隔符,那就拿分隔符后面的值,然后再去分隔符前面的值去取值。取到就用取到的值,取不到就用默认的值
5.然后把占位符换成获取到的值
这边说的可能有点绕,举个例子说下,比如要解析的是${abc:${def:gg}}这样嵌套的,
那么根据上面解析就是第一次获得abc:${def:gg},然后递归调用得到def:gg,那么就根据def:gg去PropertySource(这个是AbstractPropertyResolver之前方法中传递进来的,也就是那个getPropertyAsRawString方法)中去取数据,如果取到就用取到的值,如果取不到,就得到分隔符 : 后的gg这个默认值,然后再根据def去取,取不到就用gg这个值。然后再去把分隔符后面的值更新成取到的值如gg。那此时的值就是abc:gg。然后退出递归再次用abc:gg继续走流程。就和刚才def:gg那种一样。然后最后或得解析出来的值。
这就是PropertyPlaceholderHelper对于占位符的解析了。
总结
PropertySource是对键值的抽象,PropertyResolver是对PropertySource提供对外的统一数据处理,对于占位符的处理委托于PropertyPlaceholderHelper。介绍了这么多主要是为了继续下去springBoot源码启动里面的environment做准备的。因为environment就是包装PropertySource以及配置文件数据的地方。在spring中占有非常重要的地位。