SpringBoot源码(四): PropertySource以及解析

介绍

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中占有非常重要的地位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值