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.
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.
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.
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.
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;
}
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