2014-07-23 442 views
46

注入地圖我有一個Spring Boot應用具有以下application.yml - 從here基本上採取:彈簧引導 - 從application.yml

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

我可以注入特定值,例如

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

我想,但是,注入全圖,即是這樣的:

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

那是(或類似的東西)可能嗎?顯然,我可以直接加載yaml,但想知道Spring是否已經支持某些東西。

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

與問題的YAML運行此產生:

回答

34

你可以有一個地圖使用@ConfigurationProperties注入

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

有設置前綴,控制如何缺失的屬性的各種選項處理等等,請參閱javadoc瞭解更多信息。

+0

感謝安迪 - 這個按預期工作。有趣的是,如果沒有額外的類,它就無法工作 - 即出於某種原因(可能因爲它被用於在'SpringApplication.run'調用中運行應用程序),您無法將'info'映射放入'MapBindingSample'中。 –

+0

有沒有辦法注入子圖?例如。從上面的地圖注入'info.build'而不是'info'? –

+0

是的。將@ConfigurationProperties的前綴設置爲info,然後用名爲getBuild()的方法替換getInfo()來更新() –

12

今天我遇到了同樣的問題,但不幸的是Andy的解決方案對我無效。在Spring Boot 1.2.1.RELEASE中更容易,但你必須注意一些事情。

這是最有趣的部分從我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地圖只包含一個映射條目,我的目標是爲其他的OAuth提供者提供動態配置。我想將這個映射注入一個服務中,該服務將根據這個yaml文件中提供的配置來初始化服務。我最初的實現是:

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

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

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

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

啓動應用程序後,providers地圖在OAuth2ProvidersService未初始化。我嘗試了Andy提出的解決方案,但它並沒有奏效。我在該應用程序中使用Groovy,因此我決定刪除private並讓Groovy生成getter和setter。所以我的代碼看起來像這樣:

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

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

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

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

之後,小小的改變一切工作。

雖然有一件事值得一提。在我開始工作之後,我決定讓這個字段爲private,並在setter方法中爲setter提供直接參數類型。不幸的是,它不會工作。它會導致org.springframework.beans.NotWritablePropertyException與消息:

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? 

,如果你在Spring啓動應用程序中使用Groovy記住它。

53

下面的解決方案是@Andy Wilkinson解決方案的簡寫,不同之處在於它不必使用單獨的類或者使用帶註釋的方法。

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

} 

我們可以既俱樂部註釋@Value@ConfigurationProperties,沒有任何問題。但吸氣劑和吸附劑是重要的,並且@EnableConfigurationProperties必須具有@ConfigurationProperties才能工作。

我從@Szymon Stepniak提供的groovy解決方案試過這個想法,認爲它對於某人會有用。

+6

謝謝!我用春季啓動1.3.1,在我的情況下,我發現不需要'@ EnableConfigurationProperties' – zhuguowei

+1

非常有幫助。這是最好的答案:) – sustainablepace

+0

使用此答案時,出現「無效字符常量」錯誤。你能改變: @ConfigurationProperties(prefix ='input') 使用雙引號來防止這個錯誤。 –

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

歡迎來到堆棧溢出,而不是編寫虛擬getter&setter!儘管這段代碼可以解決這個問題,但[包括一個解釋](// meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers)確實有助於提高您的帖子的質量。請記住,您將來會爲讀者回答問題,而這些人可能不知道您的代碼建議的原因。 –