2014-07-23 7 views
46

habe ich eine Spring Boot Anwendung mit der folgenden application.yml - im Grunde von here genommen:Frühling Boot - injizieren Karte von application.yml

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

ich bestimmte Werte injizieren können, beispielsweise

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

Ich möchte jedoch die ganze Karte zu injizieren, das heißt etwas wie folgt aus:

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

Ist das (oder so ähnlich) möglich? Natürlich kann ich YAML direkt laden, habe mich aber gefragt, ob es etwas gibt, das bereits von Spring unterstützt wird.

Antwort

34

Sie können eine Karte injiziert @ConfigurationProperties mit:

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

das Laufen mit dem yaml in Frage stellt:

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

Es gibt verschiedene Optionen für ein Präfix Einstellung, Controlling, wie fehlende Eigenschaften werden behandelt, etc. Siehe die javadoc für weitere Informationen.

+0

Dank Andy - das funktioniert wie erwartet. Interessant, dass es ohne eine zusätzliche Klasse nicht funktioniert - d. H. Sie können die 'info'-Map aus irgendeinem Grund nicht in' MapBindingSample' einfügen (vielleicht, weil sie dazu verwendet wird, die App in 'SpringApplication.run' aufzurufen). –

+0

Gibt es eine Möglichkeit, eine Sub-Karte zu injizieren? Z.B. injizieren 'info.build' statt' info' von der obigen Karte? –

+0

Ja. Setze das Präfix auf @ConfigurationProperties auf info und aktualisiere dann Test, ersetze getInfo() durch eine Methode namens getBuild() –

12

Ich laufe heute auf das gleiche Problem, aber Andys Lösung hat leider nicht funktioniert. In Spring Boot 1.2.1.RELEASE ist es noch einfacher, aber Sie müssen einige Dinge beachten.

Hier ist der interessante Teil von meinem application.yml:

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

providers Karte enthält nur einen Map-Eintrag, mein Ziel ist eine dynamische Konfiguration für andere OAuth-Anbieter zur Verfügung zu stellen. Ich möchte diese Map in einen Dienst einfügen, der Dienste basierend auf der in dieser Yaml-Datei bereitgestellten Konfiguration initialisiert. Meine erste Implementierung war:

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

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

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

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

Nach dem Start der Anwendung, providers Karte in OAuth2ProvidersService wurde nicht initialisiert. Ich habe die von Andy vorgeschlagene Lösung ausprobiert, aber es hat nicht so gut funktioniert. Ich benutze Groovy in dieser Anwendung, so entschied ich, private zu entfernen und lassen Groovy Getter und Setter generieren. So sah mein Code so aus:

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

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

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

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

Nach dieser kleinen Änderung funktionierte alles.

Obwohl es eine Sache gibt, die erwähnenswert wäre. Nachdem ich es zum Laufen gebracht habe, habe ich beschlossen, dieses Feld private zu machen und Setter mit straight Argumenttyp in der Setter-Methode zur Verfügung zu stellen. Leider wird das nicht funktionieren. Es bewirkt, dass org.springframework.beans.NotWritablePropertyException mit der Meldung:

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? 

es Beachten Sie, wenn Sie Groovy in Ihrem Frühjahr Boot-Anwendung verwenden.

53

Unter Lösung ist eine Kurzschrift für @Andy Wilkinson-Lösung, außer dass es keine separate Klasse oder eine @Bean annotierte Methode verwenden muss.

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

} 

Wir können Club sowohl @Value Annotation und @ConfigurationProperties, keine Probleme. Aber Getter und Setter sind wichtig und @EnableConfigurationProperties muss das @ConfigurationProperties zu arbeiten haben.

Ich versuchte diese Idee von groovy Lösung von @Szymon Stepniak, dachte, es wird für jemanden nützlich sein.

+6

danke! Ich habe Spring Boot 1.3.1 benutzt, in meinem Fall habe ich keine '@ EnableConfigurationProperties' gefunden – zhuguowei

+1

Sehr hilfreich. Dies ist die beste Antwort :) – sustainablepace

+0

Ich bekomme einen 'ungültigen Charakter Konstante' Fehler bei der Verwendung dieser Antwort. Können Sie ändern: @ConfigurationProperties (Präfix = 'Eingabe') verwenden doppelte Anführungszeichen, um diesen Fehler zu vermeiden. –

3
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

+3

Willkommen bei Stack Overflow! Während dieses Code-Snippet die Frage lösen kann, hilft [einschließlich einer Erklärung] (// meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) wirklich, die Qualität Ihres Posts zu verbessern.Denken Sie daran, dass Sie die Frage für Leser in der Zukunft beantworten, und diese Leute könnten die Gründe für Ihren Codevorschlag nicht kennen. –

Verwandte Themen