【问题标题】:How to escape SpEL dollar signs in Spring YAML configuration?如何在 Spring YAML 配置中转义 SpEL 美元符号?
【发布时间】:2018-06-04 09:18:34
【问题描述】:

在 Spring YAML 配置文件中,我需要一个类似的参数

csv:
  file:
    pattern: /some/path/${app-name}.csv

${app-name} 在运行时被 Java 代码动态替换,我不希望 Spring 在启动时替换它。

为此,我需要转义 $ 字符,以便 Spring 不会将其解释为 SpEL

以下答案在 YAML 中有效:

我尝试了所有的组合,比如

pattern: /some/path/\${app-name}.csv
pattern: "/some/path/\${app-name}.csv"
pattern: /some/path/#{'$'}{app-name}.csv
pattern: "/some/path/#{'$'}{app-name}.csv"

并且它们都不产生包含所请求字符串的变量,带有美元符号但没有转义字符。

请注意它是 YAML 配置。在 YAML 文件中,# 是行注释字符,从该字符开始的所有内容都将被忽略。如果我使用\#,则\ 会被传递给字符串。

添加:Spring project open issue 9628 自 2008 年 6 月 25 日起开放:

目前无法注入不会被 PropertyPlaceholderConfigurer 拾取的 ${...} 表达式。理想情况下,我们应该能够注入一个包含 ${...} 的字符串以供以后在其目标 bean 中使用,而无需 PropertyPlaceholderConfigurer 的参与。

【问题讨论】:

  • 能否请您在使用csv.file.pattern 的地方发布代码?我确定第三个转义选项有效,因为我也(成功地)使用过它
  • 我使用 Spring Boot 使用 YAML 文件 (application.yml) 进行的测试已正确注入了值。我使用@Value("${csv.file.pattern}") 注解注入模式,我得到了/some/path/${app-name}.csv 结果,正如预期的那样。由于某种原因这对您不起作用,您的设置可能会有所不同,我正在尝试确定有什么区别。据我所知,可能是 YAML 库、Spring 版本等方面的差异。
  • 只是澄清一下,虽然它可能与您之后如何使用变量无关,但它绝对与您如何注入它有关,这就是我想问问。
  • @AlexSavitsky 啊哈,我用的不是@Value,而是@ConfigurationProperties
  • pattern: "/some/path/#{'$'}{app-name}.csv" 为我工作。我使用了@Value 注释。为什么不使用@Value?

标签: java spring escaping yaml spring-el


【解决方案1】:

使用空键和美元符号$ 的组合作为默认值:

csv:
  file:
    pattern: /some/path/${:$}{app-name}.csv

【讨论】:

    【解决方案2】:

    Spring 目前不提供属性占位符的转义机制,有一个open issue(于 2008 年 6 月 25 日开放)。在 cmets 中,提到了这种解决方法(我不确定它是否适用于 YAML):

    csv:
      file:
        pattern: /some/path/#{'$'}{app-name}.csv
    

    请注意,当在空格之后或行首使用时,YAML 中的 # 会开始注释。

    【讨论】:

    • 正如我在问题中所写,YAML 中的 # 导致该行的其余部分被忽略。它可以` so YAML does not interpret #`转义为行注释符号,但Java变量只包含/some/path/\#{'$'}{app-name}.csv
    • 显然这似乎不是 Spring 的高优先级:创建时间:25/Jun/08,更新时间:02/Apr/15,距离上次评论的天数:2年,38 周,1 天前 :(
    • 正如我所指出的,# 仅在前面有空格时才开始评论。在本例中并非如此。此外,您可以将标量放在双引号中。
    • 当我把它放在双引号中时,Java 变量包含的字符串与 YAML 文件中的字符串完全相同,即带有转义字符。
    • Java 框架:bug 开放 13 年,没人在意,仍在使用 JavaScript 框架:有些人不喜欢模板语法,2 周后所有人都迁移到新框架
    【解决方案3】:

    您需要使用#{'$'},并且当您使用 yaml 时,您需要用双引号将值括起来:

    csv:
      file:
        pattern: "/some/path/#{'$'}{app-name}.csv"
    

    【讨论】:

      【解决方案4】:

      我也遇到了同样的问题。因此,您可以使用 yaml 文字样式符号 "|" 或使用 literal_strip "|-" 来解决此问题,如下例所示。

      application.yml

      csv:
        file:
          pattern: |-
            /some/path/${app-name}.csv
      

      实际上我的问题是在 yml 中配置一个公式,然后在 java 中动态解析表达式。在这里分享解决方案。

      我选择spring el方案,使用spring版本5.0.9.RELEASE

      我在yml中定义了一个公式,

      score:
        formula: |-
          10 * #x + #y
      

      然后在一个spring组件bean中,

      @Value("${score.formula}")
      String scoreFormula;
      

      最后使用spring el,

      ExpressionParser parser = new SpelExpressionParser();
      EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
      
      context.setVariable("x", 1);
      context.setVariable("y", 1);
      Integer score = parser.parseExpression(scoreFormula).getValue(context,Integer.class);
      

      参考

      yaml-multi-line

      【讨论】:

        【解决方案5】:

        实际上,没有一个答案对我有用。但是,添加双美元符号对我来说很好:

        csv:
          file:
            pattern: /some/path/$${app-name}.csv
        

        【讨论】:

          【解决方案6】:

          为什么不尝试使用${sys:$},虽然丑但有效。我想没有人会用$作为key。

          【讨论】:

            【解决方案7】:

            我遇到了同样的问题,我只是找到了 dumb 聪明的解决方案 定义一个名为 DollarSign 或简称为 ds 的属性。

            ds: "$"
            

            然后像这样使用它,${ds} 将在运行时被$ 替换。

            csv:
              file:
                pattern: /some/path/${ds}{app-name}.csv
            

            当它起作用时有点有趣。

            【讨论】:

            【解决方案8】:

            答案实际上取决于您将值注入代码的准确程度。由于您尚未提供它,我将仅列出各种工作技术。

            1. 你可以使用@Value注解来注入你的属性。因为,unlike @ConfigurationProperties, @Value does SpEL evaluation,你必须逃避你的价值。

            application.yml:

            csv:
              file:
                pattern: /some/path/#{'$'}{app-name}.csv
            

            TestController.java:

            @RestController
            public class TestController {
                @Value("${csv.file.pattern}") private String pattern;
                @GetMapping("pattern") public ResponseEntity<String> getPattern() {
                    return ResponseEntity.ok(pattern);
                }
            }
            

            /pattern 的GET 将产生/some/path/#{app-name}.csv 的输出,正如您所需要的那样

            1. 您可以使用@ConfigurationProperties,这实际上取决于您如何构建配置对象。不过,一般来说,@ConfigurationProperties 不需要转义,因为默认情况下它们不支持 SpEL。但是,以下方法有效,因此如果您的设置不同,请修改它:

            application.yml:

            csv:
              file:
                pattern: /some/path/#{app-name}.csv
            

            Config.java:

            @ConfigurationProperties(prefix = "csv.file")
            public class Config {
                private String pattern;
                public String getPattern() { return pattern; }
                public void setPattern(String pattern) { this.pattern = pattern; }
            }
            

            TestController.java:

            @RestController
            public class TestController {
                @Autowired private Config config;
                @GetMapping("pattern") public ResponseEntity<String> getPattern() {
                    return ResponseEntity.ok(config.getPattern());
                }
            }
            

            同样,对 /pattern 的 GET 将导致输出 /some/path/#{app-name}.csv

            您最有可能在 Config.java 中有一些不同的结构(发布相关代码,也许?),这可能会导致属性无法正确处理。

            【讨论】:

            • 谢谢,我想我会在圣诞节后提取最少的示例代码:) 现在为时已晚...
            猜你喜欢
            • 2017-02-19
            • 2015-06-17
            • 2021-07-01
            • 1970-01-01
            • 2022-11-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多