javaspringdependency-injectionproperties-file

How do I represent a map of lists in a Spring properties file


How do I go about representing Map<String, List<String>> in a Spring .properties file?

To give an example scenario of the problem, I'd like to be able to add the following to a @Component:

@Autowired
private Map<String, List<String>> animalHabitats;

...and inject into it the following animal habitat info from a .properties file:


Solution

  • Spring does this out of the box, but arranging it is not all that intuitive.

    Create your .properties file:

    In this example we'll create a habitats.properties in /src/main/resources:

    my-habitats={ \
      home: {'dog', 'cat', 'goldfish'}, \
      farm: {'sheep', 'goat'}, \
      zoo: {'lion', 'giraffe'} \
    }
    

    Create a @Configuration class:

    @Configuration
    @PropertySource("classpath:/habitats.properties")
    public class HabitatConfiguration {
      @Bean
      public Map<String, List<String>> getHabitatConfig(@Value("#{${my-habitats}}") Map<String, List<String>> habitats) {
        return habitats;
      }
    }
    

    Inject the map into the @Component class:

    @Component
    public class HabitatValidator {
      private final Map<String, List<String>> habitats;
    
      @Autowired
      public HabitatValidator(Map<String, List<String>> habitats) {
        this.habitats = habitats;
      }
    
      public void validateInput(String habitat, String animal) throws InvalidAnimalHabitatCombinationException {
        if (!habitats.get(habitat).contains(animal)) {
          throw new InvalidAnimalHabitatCombinationException("You won't find a " + animal + " in a " + habitat);
        }
      }
    }
    

    That's it :)

    Making things a little safer

    For a little more type safety you could replace the Strings with enums. Just make sure that the values in the properties file line up with the enum entries. E.g.

    Without changing the contents of the habitats.properties file, add the following two enums:

    public enum Habitat {
      home, farm, zoo
    }
    
    public enum Animal {
      dog, cat, goldfish, sheep, goat, lion, giraffe
    }
    

    Then change the bean method in the configuration class to:

    @Bean
    public Map<Habitat, List<Animal>> getHabitatConfig(@Value("#{${my-habitats}}") Map<Habitat, List<Animal>> habitats) {
      return habitats;
    }
    

    And inject it into the component like this:

    private final Map<Habitat, List<Animal>> habitats;
    
    @Autowired
    public HabitatValidator(Map<Habitat, List<Animal>> habitats) {
      this.habitats = habitats;
    }