2014-07-23 251 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)确实有助于提高您的帖子的质量。请记住,您将来会为读者回答问题,而这些人可能不知道您的代码建议的原因。 –