简介
在spring中占位符用${}表示,他可以很好的实现将变动的数据与代码分离,这部分变化的数据就可以使用配置文件等诸多手段动态配置
spring中的占位符应用的非常广泛,比如@Value注解 @RequestMapping Feign等都支持${}
spring为该功能定义了一个接口 StringValueResolver,可以自定义实现类
可以通过beanFactory.addEmbeddedValueResolver()设置自己的解析器
默认占位符解析器 源码解析
在实例化非懒加载单例Bean之前,就会设置一个默认的占位符解析器,如下
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
其实就是从环境变量中根据key找到value,环境变量上节已经介绍过了,占位符解析代码如下
private final List<StringValueResolver> embeddedValueResolvers = new CopyOnWriteArrayList<>();public void addEmbeddedValueResolver(StringValueResolver valueResolver) {this.embeddedValueResolvers.add(valueResolver);
}public String resolveEmbeddedValue(@Nullable String value) {if (value == null) {return null;}String result = value;for (StringValueResolver resolver : this.embeddedValueResolvers) {result = resolver.resolveStringValue(result);if (result == null) {return null;}}return result;
}
遍历StringValueResolver解析器,由于spring默认值添加了一个就是上面的getEnvironment().resolvePlaceholders(strVal)。
大概逻辑就是找一下有没有占位符没有就直接返回原值,如果有就环境变量找一下,如果没有就使用默认值
值得关注的是,并不是找 ${
开头的,而是任意地方可以匹配到就行所有例如 a${b:c}
这样都是可以的
占位符定义如下
public static final String PLACEHOLDER_PREFIX = "${"; public static final String PLACEHOLDER_SUFFIX = "}";public static final String VALUE_SEPARATOR = ":"; // 看是否有默认值
resolvePlaceholders最终进入到如下代码
其实就是上节介绍的环境变量最终保存的地方,从环境变量获取
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {// propertySources就是上节介绍的各种环境变量,操作系统变量,启动参数变量,配置文件等for (PropertySource<?> propertySource : this.propertySources) {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);}}}return null;
}
测试
下面我们简单测试下
public static void main(String[] args) {System.setProperty("uri", "getOrderId");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);String value = context.getBeanFactory().resolveEmbeddedValue("/api/${uri:xx}");System.out.println(value);
}输出/api/getOrderId
自定义占位符解析
自定义占位符解析只需要实现StringValueResolver就可以了
例如
public static void main(String[] args) {System.setProperty("uri", "getOrderId");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.getBeanFactory().addEmbeddedValueResolver(strVal -> {strVal = strVal.replaceAll("\\[order]:", "");return context.getEnvironment().resolvePlaceholders(strVal);});context.register(AppConfig.class);context.refresh();String value = context.getBeanFactory().resolveEmbeddedValue("/api/[order]:${uri:xx}");System.out.println(value);}输出/api/getOrderId
总结
占位符的解析就到这里了,后面我们会介绍spring表达式
欢迎关注,学习不迷路!