9

I'm looking for a way to manipulate @ConfigurationProperties defined on initialization so that when I @Inject / @Autorwired the configured object using a @Bean method it is properly configured.

Scenario:

I have a bunch of properties set in a .yml file. Because all of these properties match as Strings some of them need special handling to be properly configured to their respective types (some of them are enums). Do I make my properties object a @Component then inject it into the @Bean method and modify it? I tried to combine @Bean and @ConfigurationProperties annotations but the object is configured AFTER the @Bean method itself returns, so any manipulation is lost/impossible. What is the best way to do this?

Example: In my .yml i have this:

properties:
  sports:
    "football": ["Team 1", "Team 2", "Team 3"]
    "basketball": ["Team 4", "Team 5", "Team 6"]
  settings:
    "football":
      "period1": "45"
      "period2": "90"
      "players": "11"
    "basketball":
      "periods": "4"
      "players": "5"

And these match with the following objects:

Map<SportsEnum, List<TeamsEnum>
Map<SportsEnum, Map<SportSettingsEnum, String>>

TL;DR: I want to take an object configured from a .yml/.properties file and manipulate its injectable representation. Please provide concrete example!

3
  • 3
    Spring will do the conversion for you, you don't need to do manual munging. String to enum is supported out-of-the-box just specify the correct enum name/value.
    – M. Deinum
    Commented Jul 26, 2016 at 7:31
  • hmm it doesn't seem to work for me though :S. Note that the configuration is of Map<EnumKey, List<EnumValue>> or even Map<EnumKey1, Map<EnumKey2, EnumValue>>
    – PentaKon
    Commented Jul 26, 2016 at 7:49
  • 1
    Ah but that is a whole different beast and goes beyond the basic conversion support (you might want to add that to your question...). With a sample of your .yml and what you expect in java.
    – M. Deinum
    Commented Jul 26, 2016 at 7:51

1 Answer 1

7

This is definitely supported, my guess is that you're doing something wrong. Let's go step by step.

First, your YAML file is badly formatted, it should look something like this:

properties:
  sports:
    football:
      - Team1
      - Team2
      - Team3
    basketball:
      - Team4
      - Team5
      - Team6
  settings:
    football:
      period1: 45
      period2: 90
      players: 11
    basketball:
      periods: 4
      players: 5

Then, your configuration properties would look like this:

@ConfigurationProperties(prefix = "properties", locations = "classpath:sports.yml")
public class SportsProperties {

    private Map<SportsEnum, List<TeamsEnum>> sports;
    private Map<SportsEnum, Map<SportSettingsEnum, String>> settings;

    public Map<SportsEnum, List<TeamsEnum>> getSports() {
        return sports;
    }

    public void setSports(Map<SportsEnum, List<TeamsEnum>> sports) {
        this.sports = sports;
    }

    public Map<SportsEnum, Map<SportSettingsEnum, String>> getSettings() {
        return settings;
    }

    public void setSettings(
        Map<SportsEnum, Map<SportSettingsEnum, String>> settings) {
        this.settings = settings;
    }

    @PostConstruct
    public void customManipulation() {
        System.out.println(sports);
        System.out.println(settings);
    }
}

Next, the properties should be enabled in your configuration:

@SpringBootApplication
@EnableConfigurationProperties(SportsProperties.class)
public class YamlSampleApplication {

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

}

That should be it, really. Here are my enums:

public enum SportsEnum {
    football, basketball
}

public enum TeamsEnum {
    Team1, Team2, Team3, Team4, Team5, Team6
}

public enum SportSettingsEnum {
    periods, period1, period2, players
}

Here's the output I get in the logs:

2016-07-26 17:44:41.226 DEBUG 30015 --- [           main] s.b.e.YamlPropertySourceLoader$Processor : Loading from YAML: class path resource [sports.yml]
2016-07-26 17:44:41.282 DEBUG 30015 --- [           main] s.b.e.YamlPropertySourceLoader$Processor : Matched document with default matcher: {properties={sports={football=[Team1, Team2, Team3], basketball=[Team4, Team5, Team6]}, settings={football={period1=45, period2=90, players=11}, basketball={periods=4, players=5}}}}
2016-07-26 17:44:41.282 DEBUG 30015 --- [           main] s.b.e.YamlPropertySourceLoader$Processor : Loaded 1 document from YAML resource: class path resource [sports.yml]
{football=[Team1, Team2, Team3], basketball=[Team4, Team5, Team6]}
{football={period1=45, period2=90, players=11}, basketball={periods=4, players=5}}
5
  • This is solves my problem of HAVING to configure the properties but it doesn't answer the specific question and I can't mark it as an answer. Thanks for your insight however.
    – PentaKon
    Commented Jul 28, 2016 at 6:37
  • Doesn't it? You just need to put whatever modifications you want into the @PostConstruct method. Though I now see that I've never mentioned this in the answer :) Commented Jul 28, 2016 at 6:41
  • Indeed I didn't think about that.
    – PentaKon
    Commented Jul 28, 2016 at 7:25
  • Is there a way to make this @PostConstruct stuff work for inner classes? (The parent class has the ConfigurationProperties annotation already)
    – takanuva15
    Commented Sep 26, 2023 at 16:10
  • @takanuva15 @PostConstruct can only work on objects managed by Spring (beans, configurations, configuration properties, etc). If your inner class isn't managed by Spring, then it won't work Commented Sep 27, 2023 at 11:53

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.