spring之PropertySourcesPlaceholdersResolver

概述

我们在使用spring的时候可以通过占位符 获取系统变量,如:@Value(“${test.produce}”)

test.name=张三
test.age=28
test.produce=${test.name}:${test.age}

实例

配置文件

application.properties

my.home=地球
my.location=${my.home}
my.address=${my.location:我来自哪里}
代码
@EnableScheduling
@SpringBootApplication
public class Application {
   public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(Application.class, args);
       PropertySourcesPlaceholdersResolver resolver = new PropertySourcesPlaceholdersResolver(context.getEnvironment());
       val key = "${my.address}";
       System.out.println(resolver.resolvePlaceholders(key));
   }
} 
结果输出

地球
如果环境变量my.location不存在将输出:我来自哪里

原理分析

PropertySourcesPlaceholdersResolver构造函数,指定占位符的前缀后缀默认值分隔符未解析忽略环境变量容器

	public PropertySourcesPlaceholdersResolver(Environment environment) {
   	this(getSources(environment), null);
   }

   public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources) {
   	this(sources, null);
   }
    /**
     * PropertyPlaceholderHelper解析辅助类
     *      placeholderPrefix 占位符前缀
     *      placeholderSuffix 占位符后缀
     *      simplePrefix 前缀简写
     *     valueSeparator 默认值分隔符
     *     ignoreUnresolvablePlaceholders 是否忽略未解析成功
    */
   public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources, PropertyPlaceholderHelper helper) {
   	this.sources = sources;
   	this.helper = (helper != null) ? helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX,
   			SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true);
   }

PropertyPlaceholderHelper#parseStringValue占位符的解析主要在这个方法里完成

protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
        //获取占位符的前缀索引,如果索引为-1,则说明不存在占位符直接返回  
		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}

		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
		    //查找占位符的后缀索引,findPlaceholderEndIndex方法下面会有单独解析
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			//如果解析到后缀索引,则继续处理
			if (endIndex != -1) {
			    //获取占位符内需要解析的字段,如:${my.name}返回的结果为my.name
				String placeholder = result.substring(startIndex +this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				//判断占位符是否存在循环引用的情况,如:
				// your.name=${my.name}
		        // my.name=${your.name}
				//如果则抛出异常
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				//递归解析占位符中包含占位符,如:
				//     enviroment.online=test1
				//     enviroment.offline=test2
				//     current.enviroment=online
				//     project.enviroment=${current.enviroment.${current.enviroment}}
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				//获取占位符对应的值,resolvePlaceholder方法下面会做具体解析
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				//如果没有解析到占位符的值,则看是否有默认值配置,如:
				// my.name=${your.name:June}
				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) {
				   //递归调用解析之前解析到的值中存在的占位符
					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) {
				    //继续解析未解析的占位符(一段字符串中可能包含多个占位符)
					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();
	}

PropertyPlaceholderHelper#findPlaceholderEndIndex寻找带占位符结束索引

	private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
		int index = startIndex + this.placeholderPrefix.length();
		int withinNestedPlaceholder = 0;
		while (index < buf.length()) {
		     //判断字符串buf索引index是否为placeholderSuffix结束占位符
			if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
			    //如果存在还未解析的且套占位符如:
			    //project.enviroment=${current.enviroment.${current.enviroment}}
			    //则需继续解析
				if (withinNestedPlaceholder > 0) {
					withinNestedPlaceholder--;
					//已解析的索引继续移动placeholderSuffix.length()位置,继续解析
					index = index + this.placeholderSuffix.length();
				}
				else {
					return index;
				}
			}
			//如果字符串buf的索引index的值为开始占位符的简写,说明存在嵌套占位符的情况
			else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
				withinNestedPlaceholder++;
				//索引移动simplePrefix.length()个字符继续解析
				index = index + this.simplePrefix.length();
			}
			else {
				index++;
			}
		}
		return -1;
	}

PropertySourcesPlaceholdersResolver#resolvePlaceholder从上下文环境变量容器中查找对应的变量值,后续会写spring上下文中有哪些PropertySource及加载实现方式
在这里插入图片描述

    //这个soueces是各种环境变量的集合如:
    // application.properties配置文件
    // application-xx.yml配置文件
    // systemProperties 系统环境变量
    // systemEviroment 操作系统环境变量等
	protected String resolvePlaceholder(String placeholder) {
		if (this.sources != null) {
			for (PropertySource<?> source : this.sources) {
				Object value = source.getProperty(placeholder);
				if (value != null) {
					return String.valueOf(value);
				}
			}
		}
		return null;
	}
	
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值