可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a Spring Boot application with the following application.yml - taken basically from here:
info: build: artifact: ${project.artifactId} name: ${project.name} description: ${project.description} version: ${project.version}
I can inject particular values, e.g.
@Value("${info.build.artifact}") String value
I would like, however, to inject the whole map, i.e. something like this:
@Value("${info}") Map info
Is that (or something similar) possible? Obviously, I can load yaml directly, but was wondering if there's something already supported by Spring.
回答1:
You can have a map injected using @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 info = new HashMap(); public Map getInfo() { return this.info; } } }
Running this with the yaml in the question produces:
{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}}
There are various options for setting a prefix, controlling how missing properties are handled, etc. See the javadoc for more information.
回答2:
Below solution is a shorthand for @Andy Wilkinson's solution, except that it doesn't have to use a separate class or on a @Bean annotated method.
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 somedata; public HashMap getSomedata() { return somedata; } public void setSomedata(HashMap somedata) { this.somedata = somedata; } }
We can club both @Value annotation and @ConfigurationProperties, no issues. But getters and setters are important and @EnableConfigurationProperties is must to have the @ConfigurationProperties to work.
I tried this idea from groovy solution provided by @Szymon Stepniak, thought it will be useful for someone.
回答3:
I run into the same problem today, but unfortunately Andy's solution didn't work for me. In Spring Boot 1.2.1.RELEASE it's even easier, but you have to be aware of a few things.
Here is the interesting part from my 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 map contains only one map entry, my goal is to provide dynamic configuration for other OAuth providers. I want to inject this map into a service that will initialize services based on the configuration provided in this yaml file. My initial implementation was:
@Service @ConfigurationProperties(prefix = 'oauth') class OAuth2ProvidersService implements InitializingBean { private Map> providers = [:] @Override void afterPropertiesSet() throws Exception { initialize() } private void initialize() { //.... } }
After starting the application, providers map in OAuth2ProvidersService was not initialized. I tried the solution suggested by Andy, but it didn't work as well. I use Groovy in that application, so I decided to remove private and let Groovy generates getter and setter. So my code looked like this:
@Service @ConfigurationProperties(prefix = 'oauth') class OAuth2ProvidersService implements InitializingBean { Map> providers = [:] @Override void afterPropertiesSet() throws Exception { initialize() } private void initialize() { //.... } }
After that small change everything worked.
Although there is one thing that might be worth mentioning. After I make it working I decided to make this field private and provide setter with straight argument type in the setter method. Unfortunately it wont work that. It causes org.springframework.beans.NotWritablePropertyException with 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?
Keep it in mind if you're using Groovy in your Spring Boot application.
回答4:
foo.bars.one.counter=1 foo.bars.one.active=false foo.bars[two].id=IdOfBarWithKeyTwo public class Foo { private Map bars = new HashMap(); public Map getBars() { .... } }
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding