SpringBoot 引入 SPEL 模板字符串替换的两种方式

本文介绍了Spring框架中的SpEL表达式语言在模板占位符替换的应用,以及使用`MapAccessor`和`PropertyPlaceholderHelper`进行占位符替换的方法,同时展示了利用ApacheCommonsText库的另一种实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring 表达式语言 (SpEL)

官方文档:https://docs.spring.io/spring-framework/docs/6.0.x/reference/html/core.html#expressions

模板占位符 替换 {$name}

import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.PropertyPlaceholderHelper;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
/**
 * 内容占位符 替换
 * <p>
 * 模板占位符格式{$name}
 */
public class ContentHolderUtil {

    /**
     * 占位符前缀
     */
    private static final String PLACE_HOLDER_PREFIX = "{$";

    /**
     * 占位符后缀
     */
    private static final String PLACE_HOLDER_SUFFIX = "}";

    private static final StandardEvaluationContext EVALUATION_CONTEXT;

    private static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = new PropertyPlaceholderHelper(
            PLACE_HOLDER_PREFIX, PLACE_HOLDER_SUFFIX);

    static {
        EVALUATION_CONTEXT = new StandardEvaluationContext();
        EVALUATION_CONTEXT.addPropertyAccessor(new MapAccessor());
    }

    public static String replacePlaceHolder(final String template, final Map<String, String> paramMap) {
        String replacedPushContent = PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(template,
                new CustomPlaceholderResolver(template, paramMap));
        return replacedPushContent;
    }

    private static class CustomPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
        private final String template;
        private final Map<String, String> paramMap;

        public CustomPlaceholderResolver(String template, Map<String, String> paramMap) {
            super();
            this.template = template;
            this.paramMap = paramMap;
        }

        @Override
        public String resolvePlaceholder(String placeholderName) {
            String value = paramMap.get(placeholderName);
            if (null == value) {
                String errorStr = MessageFormat.format("template:{0} require param:{1},but not exist! paramMap:{2}",
                        template, placeholderName, paramMap.toString());
                throw new IllegalArgumentException(errorStr);
            }
            return value;
        }
    }

    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("name","张三");
        map.put("age","12");
        // 注意:{$}内不能有空格
        final String s = replacePlaceHolder("我叫 {$name}, 我今年 {$age} 岁了。", map);
        System.out.println(s);
    }
}

common-text 方式:模板字符转替换 ${}

  1. 添加依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.10.0</version>
    </dependency>
    
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
  2. 测试

    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.text.StringSubstitutor;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.StringUtils;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    @Slf4j
    public class CommonTextUtil {
        // 占位符前缀
        private static final String prefix = "${";
        // 占位符后缀
        private static final String suffix = "}";
    
        /*
         * commons-text
         * */
        public static String replaceVar(Map<String, Object> vars, String template) {
            if (!StringUtils.hasLength(template)) {
                log.info(String.format("调用%s方法失败,模板字符串替换失败,模板字符串不能为空",
                        Thread.currentThread().getStackTrace()[1].getMethodName()));
                return null;
            }
            if (CollectionUtils.isEmpty(vars)) {
                log.info(String.format("调用%s方法失败,模板字符串替换失败,map不能为空",
                        Thread.currentThread().getStackTrace()[1].getMethodName()));
                return null;
            }
            List<String> tempStrs = vars.keySet().stream().map(s -> prefix + s + suffix).
                    collect(Collectors.toList());
            tempStrs.forEach(t -> {
                if (!template.contains(t)) {
                    throw new RuntimeException(String.format("调用%s方法失败,模板字符串替换失败,map的key必须存在于模板字符串中",
                            Thread.currentThread().getStackTrace()[1].getMethodName()));
                }
            });
            StringSubstitutor stringSubstitutor = new StringSubstitutor(vars);
            return stringSubstitutor.replace(template);
        }
    
        public static void main(String[] args) {
            /*
             * 错误的场景:比如${变量},{}内不能含有空格等等
             * System.out.println(replaceVar(vals, "我叫${ name},今年${age }岁."));
             * System.out.println(replaceVar(new HashMap<>(), temp));
             * System.out.println(replaceVar(vals, "我叫张三"));
             * System.out.println(replaceVar(vals, "我叫${name},今年${age1}岁."));
             * */
            Map<String, Object> vals = new HashMap<>();
            vals.put("name", "张三");
            vals.put("age", "20");
            String temp = "我叫${name},今年${age}岁.";
            System.out.println(replaceVar(vals, temp));
        }
    }
    
### Spring SPEL 模板字符串中的特殊符号处理 在Spring表达式语言(SPEL)中,当需要在模板字符串内使用某些具有特定含义的字符时,这些字符可能会干扰SPEL解析器的工作流程。为了确保这些字符能够按照预期作为普通文本的一部分来对待而不是被解释成语法成分,可以采用反斜杠`\`来进行转义操作[^2]。 对于一些常见的控制字符和其他有特殊意义的符号,在SPEL表达式的字符串里可以通过前缀加反斜杠的方式让它们失去原有的功能而成为普通的打印字符。例如: - 若要表示实际意义上的单引号或双引号,则可以在其前面加上反斜杠; - 对于像换行(`\n`)、制表符(`\t`)这样的不可见字符同样适用此方法; 值得注意的是,在涉及到Map类型的属性设置过程中,如果键名包含了非字母数字、连字符(-)或是句点(.)之外的其他字符,那么应当利用方括号([])将整个键包裹起来以防止其中含有的特殊字符遭到误识别并去除掉[^1]。 下面给出一段Java代码片段展示如何在SpEL表达式中安全地嵌入特殊字符: ```java String expression = "'This is a test string with special chars like \\, ', \" and newline:\\n'"; ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); parser.parseExpression(expression).getValue(context); ``` 上述例子展示了怎样通过双重反斜杠来代表一个真实的反斜杠本身,并且也说明了如何正确引入单引号和双引号以及创建新的行。 #### 使用示例 假设存在如下场景:有一个名为`configProps`的地图对象,其中一个条目的键为`my.key.with.dot`,对应的值是一个带有各种特殊字符(包括但不限于空白空间、逗号、分号等)的字符串。此时应该这样编写相应的SpEL语句: ```java @Value("#{${configProps['my\\.key\\.with\\.dot']}}") private String mySpecialConfig; ``` 这里不仅对方括号进行了必要的应用以保护含有`.`在内的复合型键名称不被破坏,同时也对内部出现的小数点做了额外的一层转义处理,从而使得最终获取到的就是原封不动的目标配置项内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

师小师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值