Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上相关的知识,希望对你有一定的参考价值。

Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上


属性解析

PropertyResolver—屏蔽不同底层属性源的获取属性接口的不同

先说一下底层属性源的基本概念: 就像数据库底层可以是连接mysql,也可以是orcale,还可以是nosql数据库,例如: redis,这里也是同样的,属性可以来自配置文件,jdk环境变量和系统属性,还可以来自其他自定义的属性源,但是正如jdbc规定了统一访问数据库的接口一样,spring也是通过PropertyResolver统一规定了访问属性源里面属性的统一接口而已

/**
屏蔽不同底层属性源的获取属性接口的不同
 */
public interface PropertyResolver 

	/**
返回给定的属性键是否可用于解析
	 */
	boolean containsProperty(String key);

	/**
      返回与给定键关联的属性值,如果无法解析键,则返回 null。
	 */
	@Nullable
	String getProperty(String key);

	/**
返回与给定键关联的属性值,如果无法解析键,则返回 defaultValue。
	 */
	String getProperty(String key, String defaultValue);

	/**
返回与给定键关联的属性值,如果无法解析键,则返回 null。
targetType:属性值的预期类型
	 */
	@Nullable
	<T> T getProperty(String key, Class<T> targetType);


	/**
	返回key关联的value,如果key解析不了,抛出异常
	 */
	String getRequiredProperty(String key) throws IllegalStateException;


	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

///

	/**
解析给定文本中的 ... 占位符,用 getProperty 解析的相应属性值替换它们。
没有默认值的不可解析占位符将被忽略并保持不变。
	 */
	String resolvePlaceholders(String text);

	/**
解析给定文本中的 ... 占位符,用 getProperty 解析的相应属性值替换它们。没有默认值的不可解析占位符将导致抛出 IllegalArgumentException
	 */
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;


PropertyResolver顶层接口规定了获取属性和解析占位符的相关方法,基本都是只读方法,这也是spring的一贯作风,底层接口只规定只读方法


ConfigurablePropertyResolver—屏蔽不同底层属性源的获取属性接口的不同

ConfigurablePropertyResolver继承了PropertyResolver,主要提供写的方法,在spring中,可写意味着可配置,因此这些类通常以Configurable开头

/**
大多数(如果不是全部)PropertyResolver 类型都将实现的配置接口。
提供用于访问和自定义将属性值从一种类型转换为另一种类型时使用的 ConversionService 的工具。
 */
public interface ConfigurablePropertyResolver extends PropertyResolver 

	/**
ConversionService主要用户属性的类型转换---一会我们会分析一下这个类
	 */
	ConfigurableConversionService getConversionService();

	/**
注意:作为完全替换 ConversionService 的替代方法,
请考虑通过深入了解 getConversionService() 并调用诸如 addConverter 之类的方法来添加或删除单个 Converter 实例。
See Also:
PropertyResolver.getProperty(String, Class), 
getConversionService(),
org.springframework.core.convert.converter.ConverterRegistry.addConverter
	 */
	void setConversionService(ConfigurableConversionService conversionService);


//

	/**
设置被此解析器替换的占位符必须以前缀开头。 ---> $
	 */
	void setPlaceholderPrefix(String placeholderPrefix);

	/**
后缀---> 
	 */
	void setPlaceholderSuffix(String placeholderSuffix);

	/**
指定由此解析器替换的占位符与其关联的默认值之间的分隔字符,如果不应将此类特殊字符作为值分隔符处理,则为 null。
$port:8080--->默认为: --->如果port对于的key解析不存在,则使用:后面的默认值
	 */
	void setValueSeparator(@Nullable String valueSeparator);

	/**
    如果占位符解析不了,是否要忽略,true,原样保留,false,抛出异常
	 */
	void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

	/**
    哪些属性必须存在,不然死啦啦滴干活
	 */
	void setRequiredProperties(String... requiredProperties);

	/**
验证 setRequiredProperties 指定的每个属性是否存在并解析为非空值。
	 */
	void validateRequiredProperties() throws MissingRequiredPropertiesException;



基本上在PropertyResolver 获取属性的基础上,增加了属性转换器接口的规定和一下解析器接口相关配置的规定


AbstractPropertyResolver —用于针对任何底层属性源解析属性的抽象基类。

/**
用于针对任何底层属性源解析属性的抽象基类。
 */
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;

//$port:8080--->分割key和默认值的分隔符-->默认是:
	@Nullable
	private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;

//必须存在的属性---这些属性需要被校验
	private final Set<String> requiredProperties = new LinkedHashSet<>();

//尝试获取一个属性转换器,如果不存在就利用双重锁来避免重复创建
	@Override
	public ConfigurableConversionService getConversionService() 
		ConfigurableConversionService cs = this.conversionService;
		if (cs == null) 
			synchronized (this) 
				cs = this.conversionService;
				if (cs == null) 
				//默认的转换器
					cs = new DefaultConversionService();
					this.conversionService = cs;
				
			
		
		return cs;
	

//设置转换器---建议不要,一会我们对ConversionService进行分析的时候,大家就懂了
	@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) 
		Collections.addAll(this.requiredProperties, requiredProperties);
	

//验证RequiredPropertie集合里面的属性是否都存在
	@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);
	

	/**
解析给定字符串中的占位符,根据 setIgnoreUnresolvableNestedPlaceholders
 的值来确定任何不可解析的占位符是否应该引发异常或被忽略。
	 */
	protected String resolveNestedPlaceholders(String value) 
		if (value.isEmpty()) 
			return 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) 
			//如果首先不需要标准类型转换,请避免初始化共享 DefaultConversionService...
			if (ClassUtils.isAssignableValue(targetType, value)) 
				return (T) value;
			
			conversionServiceToUse = DefaultConversionService.getSharedInstance();
		
		//进行类型转换
		return conversionServiceToUse.convert(value, targetType);
	


	/**
将指定的属性作为原始字符串检索,即不解析占位符
	 */
	@Nullable
	protected abstract String getPropertyAsRawString(String key);


该类主要是对两个父接口的功能做出了具体的实现,但是因为底层数据源不同,因此具体的属性值获取功能还是要交给下面不同数据源的子类来完成


PropertySourcesPropertyResolver----实现从数据源中获取属性的方法
/**
根据一组基础 PropertySource(属性源集合) 解析属性值的 PropertyResolver 实现
 */
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver 

    //属性源集合
	@Nullable
	private final PropertySources propertySources;


	/**
	 * 通过给定的一组属性源来创建属性解析器
	 */
	public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) 
		this.propertySources = propertySources;
	


   //遍历每个属性源,询问是否包含指定要查看的key
	@Override
	public boolean containsProperty(String key) 
		if (this.propertySources != null) 
			for (PropertySource<?> propertySource : this.propertySources) 
				if (propertySource.containsProperty(key)) 
					return true;
				
			
		
		return false;
	

//getProperty默认都会去解析占位符的
	@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);
	

//属性key,返回value类型,是否解析占位符
//该方法还可以看出一点,就是数据源在集合中位置的先后问题,因为是轮询遍历,只要找到一个符合的就直接返回,因此属性源的先后问题
//很重要,不注意的话,会产生属性覆盖的问题
	@Nullable
	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) 
		if (this.propertySources != null) 
		//依旧是遍历每个数据源
			for (PropertySource<?> propertySource : this.propertySources) 
				if (logger.isTraceEnabled()) 
					logger.trace("Searching for key '" + key + "' in PropertySource '" +
							propertySource.getName() + "'");
				
				//尝试从每个数据源去寻找指定的key对应的value
				Object value = propertySource.getProperty(key);
				if (value != null) 
				//如果对于value存在,并且value是string类型并且需要解析占位符
					if (resolveNestedPlaceholders && value instanceof String) 
						//解析value里面的占位符---如果存在的话
						value = resolveNestedPlaceholders((String) value);
					
					logKeyFound(key, propertySource, value);
					//如果需要的话,会进行类型转换
					return convertValueIfNecessary(value, targetValueType);
				
			
		
		if (logger.isTraceEnabled()) 
			logger.trace("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());
	

以上是关于Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上的主要内容,如果未能解决你的问题,请参考以下文章

Spring读源码系列番外篇08---BeanWrapper没有那么简单--中

Spring读源码系列番外篇---06----类型转换---下---ConversionService相关家族

Spring读源码系列番外篇---03---PropertyResolver的结构体系剖析---下

Spring读源码系列番外篇---02---PropertyResolver的结构体系剖析---上

Spring读源码系列番外篇---05----类型转换---中---三种全新的类型转换器

重学springboot系列番外篇之RestTemplate