24. Spring源码篇之占位符填充${}

简介

在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表达式


欢迎关注,学习不迷路!

你可能感兴趣的:(spring,framework,spring,java)