Spring Загрузка - введите карту из application.yml
У меня есть приложение Spring Boot со следующим 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.
Ответы
Ответ 1
Вы можете ввести карту с помощью @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;
}
}
}
Запуск этого с помощью yaml в вопросе вызывает:
{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}}
Существуют различные варианты настройки префикса, управления обработкой отсутствующих свойств и т.д. Для получения дополнительной информации см. javadoc.
Ответ 2
Ниже решение является сокращением для решения @Andy Wilkinson, за исключением того, что ему не нужно использовать отдельный класс или аннотированный метод @Bean
.
application.yml выглядит следующим образом
input:
name: raja
age: 12
somedata:
abcd: 1
bcbd: 2
cdbd: 3
@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
для работы.
Я пробовал эту идею из решения groovy, предоставленного @Szymon Stepniak, подумал, что это будет полезно для кого-то.
Ответ 3
Сегодня я столкнулся с той же проблемой, но, к сожалению, решение для 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
map в OAuth2ProvidersService
не был инициализирован. Я попробовал решение, предложенное Энди, но это не сработало. Я использую 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?
Имейте это в виду, если вы используете Groovy в своем приложении для загрузки Spring.
Ответ 4
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