How to get nested Spel expression based objects through @ConfigurationProperties in Spring Boot?

huangapple go评论111阅读模式
英文:

How to get nested Spel expression based objects through @ConfigurationProperties in Spring Boot?

问题

这是我的 application.yml 文件内容:

translations:
  en:
    title: 'english'
    description: 'english text'
  fr:
    title: 'french'
    description: 'french text'
  de:
    title: 'german'
    description: 'french text'

codes:
  GB:
    en: ${translations.en}
  CA:
    en: ${translations.en}
    fr: ${translations.fr}
  DE:
    en: ${translations.en}
    fr: ${translations.fr}
    de: ${translations.de}

现在考虑这段代码(为简洁起见,删除了不必要的 getter 方法,并且使用了 Project Lombok):

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
    // 内部类,但这些也可以放在外面
    @ConfigurationProperties
    @Getter
    @Component
    class Config {
        Map<String, Map<String, LanguageDTO>> codes;
    }

    @Data
    @AllArgsConstructor
    class LanguageDTO {
        String title;
        String description;
    }

    @Autowired
    Config config;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // 在下面的这行代码上出现了错误
        Map<String, Map<String, LanguageDTO>> codes = config.getCodes();
    }
}

现在当我启动应用程序时,我会收到如下错误:

APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'codes.gb.en' to com.example.demo.DemoApplication$LanguageDTO:

    Property: codes.gb.en
    Value: ${translations.en}
    Origin: class path resource [application.yml]:24:9
    Reason: No converter found capable of converting from type [java.lang.String] to type [com.example.demo.DemoApplication$LanguageDTO]

Action:

Update your application's configuration

我的需求是什么?

我希能够将 codes 读取为一个 Map<String, Map<String, LanguageDTO>>。也就是说,我应该能够执行 config.getCodes().get("GB").get("en") --> 这应该返回一个类型为 LanguageDTO 的数据。

英文:

This is in my application.yml

translations:
  en:
    title: &#39;english&#39;
    description: &#39;english text&#39;
  fr:
    title: &#39;french&#39;
    description: &#39;french text&#39;
  de:
    title: &#39;german&#39;
    description: &#39;french text&#39;

codes:
  GB:
    # I am assuming that en here will have an object with both title and description properties
    en: ${translations.en}
  CA:
    en: ${translations.en}
    fr: ${translations.fr}
  DE:
    en: ${translations.en}
    fr: ${translations.fr}
    de: ${translations.de}

Now consider this piece of code(removed unnecessary getters etc and also using project lombok for brevity)

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
    // Inner classes, but these can be outside as well
    @ConfigurationProperties
    @Getter
    @Component
    class Config {
        Map&lt;String, Map&lt;String, LanguageDTO&gt;&gt; codes;
    }

    @Data
    @AllArgsConstructor
    class LanguageDTO {
        String title;
        String description;
    }

    @Autowired
    Config config;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // Getting error on this line below
        Map&lt;String, Map&lt;String, LanguageDTO&gt;&gt; codes = config.getCodes();
    }
}

Now when I start the app, I am getting an error like this:

APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under &#39;codes.gb.en&#39; to com.example.demo.DemoApplication$LanguageDTO:

    Property: codes.gb.en
    Value: ${translations.en}
    Origin: class path resource [application.yml]:24:9
    Reason: No converter found capable of converting from type [java.lang.String] to type [com.example.demo.DemoApplication$LanguageDTO]

Action:

Update your application&#39;s configuration

What I want?

I want to be able to read codes as a Map&lt;String, Map&lt;String, LanguageDTO&gt;. That is, I should be able to do config.getCodes().get(&quot;GB&quot;).get(&quot;en&quot;) --> which should in turn have a data type of LanguageDTO.

答案1

得分: 1

我认为这是不可行的:Spring仅在application.yaml文件中支持简单的属性占位符(就我所知)。不过,你可以利用YAML格式的内置功能,使用锚点和别名:

translations:
  en: &en
    title: '英文'
    description: '英文文本'
  fr: &fr
    title: '法文'
    description: '法文文本'
  de: &de
    title: '德文'
    description: '法文文本'

codes:
  GB:
    en: *en
  CA:
    en: *en
    fr: *fr
  DE:
    en: *en
    fr: *fr
    de: *de

为使其工作,LanguageDTO类需要具有setter方法和默认构造函数。@AllArgsConstructor不起作用。或者你可以尝试与constructor binding一起使用,尽管我不确定是否可行。

英文:

I don't think it's doable: Spring supports only simple property placeholders inside the application.yaml file (as far as I know). What you could do though, is leveraging YAML format built-in functionality, anchors and aliases:

translations:
  en: &amp;en
    title: &#39;english&#39;
    description: &#39;english text&#39;
  fr: &amp;fr
    title: &#39;french&#39;
    description: &#39;french text&#39;
  de: &amp;de
    title: &#39;german&#39;
    description: &#39;french text&#39;

codes:
  GB:
    en: *en
  CA:
    en: *en
    fr: *fr
  DE:
    en: *en
    fr: *fr
    de: *de

> For it to work, the LanguageDTO class needs to have setters and a default constructor. @AllArgsConstructor won't work. Alternatively you might try to get it work in conjunction with constructor binding, although I'm not sure if it's possible.

huangapple
  • 本文由 发表于 2020年10月15日 02:34:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/64359514.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定