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<String, Object> info
Is that (or something similar) possible? Obviously, I can load yaml directly, but was wondering if there's something already supported by Spring.
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<String, Integer> somedata;
public HashMap<String, Integer> getSomedata() {
return somedata;
}
public void setSomedata(HashMap<String, Integer> 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.
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<String, Object> info = new HashMap<String, Object>();
public Map<String, Object> 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.
To retrieve map from configuration you will need configuration class. @Value annotation won't do the trick, unfortunately.
Application.yml
entries:
map:
key1: value1
key2: value2
Configuration class:
@Configuration
@ConfigurationProperties("entries")
@Getter
@Setter
public static class MyConfig {
private Map<String, String> map;
}
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<String, Map<String, String>> 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<String, Map<String, String>> 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With