在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表达式
欢迎关注,学习不迷路!