web-dev-qa-db-fra.com

Spring Boot - injecte la carte à partir de application.yml

J'ai un Spring Boot application avec le suivant application.yml - pris essentiellement de ici :

info:
   build:
      artifact: ${project.artifactId}
      name: ${project.name}
      description: ${project.description}
      version: ${project.version}

Je peux injecter des valeurs particulières, par exemple.

@Value("${info.build.artifact}") String value

Je voudrais cependant injecter toute la carte, c'est-à-dire quelque chose comme ceci:

@Value("${info}") Map<String, Object> info

Est-ce possible (ou quelque chose de similaire)? Évidemment, je peux charger yaml directement, mais je me demandais si quelque chose était déjà supporté par Spring.

77
levant pied

Vous pouvez avoir une carte injectée en utilisant @ConfigurationProperties:

import Java.util.HashMap;
import Java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties
public class MapBindingSample {

    public static void main(String[] args) throws Exception {
        System.out.println(SpringApplication.run(MapBindingSample.class, args)
                .getBean(Test.class).getInfo());
    }

    @Bean
    @ConfigurationProperties
    public Test test() {
        return new Test();
    }

    public static class Test {

        private Map<String, Object> info = new HashMap<String, Object>();

        public Map<String, Object> getInfo() {
            return this.info;
        }
    }
}

Exécuter ceci avec le yaml dans la question produit:

{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}}

Il existe différentes options pour définir un préfixe, contrôler le traitement des propriétés manquantes, etc. Voir le javadoc pour plus d'informations.

60
Andy Wilkinson

La solution ci-dessous est un raccourci pour la solution de @Andy Wilkinson, à ceci près qu'il n'est pas nécessaire d'utiliser une classe séparée ou sur une méthode annotée @Bean.

application.yml:

input:
  name: raja
  age: 12
  somedata:
    abcd: 1 
    bcbd: 2
    cdbd: 3

SomeComponent.Java:

@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "input")
class SomeComponent {

    @Value("${input.name}")
    private String name;

    @Value("${input.age}")
    private Integer age;

    private HashMap<String, Integer> somedata;

    public HashMap<String, Integer> getSomedata() {
        return somedata;
    }

    public void setSomedata(HashMap<String, Integer> somedata) {
        this.somedata = somedata;
    }

}

Nous pouvons associer les deux annotations @Value et @ConfigurationProperties, sans problème. Mais les getters et les setters sont importants et le @EnableConfigurationProperties est indispensable pour que le @ConfigurationProperties fonctionne.

J'ai essayé cette idée de la solution groovy fournie par @Szymon Stepniak, pensant qu'elle serait utile pour quelqu'un.

87
raksja

Je rencontre le même problème aujourd'hui, mais malheureusement, la solution d'Andy n'a pas fonctionné pour moi. Dans Spring Boot 1.2.1.LELEASE, c'est encore plus facile, mais vous devez être conscient de certaines choses.

Voici la partie intéressante de mon application.yml:

oauth:
  providers:
    google:
     api: org.scribe.builder.api.Google2Api
     key: api_key
     secret: api_secret
     callback: http://callback.your.Host/oauth/google

La carte providers ne contient qu'une seule entrée de carte. Mon objectif est de fournir une configuration dynamique aux autres fournisseurs OAuth. Je veux injecter cette carte dans un service qui initialisera les services en fonction de la configuration fournie dans ce fichier yaml. Ma mise en œuvre initiale était:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    private Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

Après le démarrage de l'application, la carte providers dans OAuth2ProvidersService n'a pas été initialisée. J'ai essayé la solution proposée par Andy, mais cela n'a pas fonctionné aussi bien. J'utilise Groovy dans cette application. J'ai donc décidé de supprimer private et de laisser Groovy générer le getter et le setter. Donc, mon code ressemblait à ceci:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

Après ce petit changement, tout a fonctionné.

Bien qu'il y ait une chose qui mérite d'être mentionnée. Après que cela fonctionne, j'ai décidé de rendre ce champ private et de fournir à setter un type d'argument simple dans la méthode setter. Malheureusement, cela ne fonctionnera pas. Cela provoque org.springframework.beans.NotWritablePropertyException avec le message:

Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Cannot access indexed value in property referenced in indexed property path 'providers[google]'; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Bean property 'providers[google]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

Gardez cela à l'esprit si vous utilisez Groovy dans votre application Spring Boot.

16
Szymon Stepniak

Pour récupérer la carte de la configuration, vous aurez besoin de la classe de configuration. L'annotation @Value ne fera pas l'affaire, malheureusement.

Application.yml

entries:
  map:
     key1: value1
     key2: value2

Classe de configuration:

@Component
    @ConfigurationProperties("entries")
    @Getter
    @Setter
    public static class MyConfig {
        private Map<String, String> map;
    }
7
Orbite
foo.bars.one.counter=1
foo.bars.one.active=false
foo.bars[two].id=IdOfBarWithKeyTwo

public class Foo {

  private Map<String, Bar> bars = new HashMap<>();

  public Map<String, Bar> getBars() { .... }
}

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding

2
emerson moura