[英]Spring Boot - inject map from application.yml
我有一個帶有以下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 是否已經支持某些內容。
下面的解決方案是@Andy Wilkinson 解決方案的簡寫,只是它不必使用單獨的類或@Bean
注釋方法。
應用程序.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
,沒問題。 但是 getter 和 setter 很重要, @EnableConfigurationProperties
必須讓@ConfigurationProperties
才能工作。
我從@Szymon Stepniak 提供的 groovy 解決方案中嘗試了這個想法,認為它對某人有用。
您可以使用@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 。
要從配置中檢索地圖,您將需要配置類。 不幸的是,@Value 注釋不起作用。
應用程序.yml
entries:
map:
key1: value1
key2: value2
配置類:
@Configuration
@ConfigurationProperties("entries")
@Getter
@Setter
public static class MyConfig {
private Map<String, String> map;
}
我今天遇到了同樣的問題,但不幸的是安迪的解決方案對我不起作用。 在 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() {
//....
}
}
啟動應用程序后, OAuth2ProvidersService
中的providers
映射未初始化。 我嘗試了安迪建議的解決方案,但效果不佳。 我在那個應用程序中使用了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 Boot 應用程序中使用 Groovy,請記住這一點。
使用@Value從application.yml屬性中提取Map 的解決方案編碼為多行
應用程序.yml
other-prop: just for demo
my-map-property-name: "{\
key1: \"ANY String Value here\", \
key2: \"any number of items\" , \
key3: \"Note the Last item does not have comma\" \
}"
other-prop2: just for demo 2
這里,我們的地圖屬性“my-map-property-name”的值以JSON格式存儲在一個字符串中,我們在行尾使用\\ 實現了多行
我的JavaClass.java
import org.springframework.beans.factory.annotation.Value;
public class myJavaClass {
@Value("#{${my-map-property-name}}")
private Map<String,String> myMap;
public void someRandomMethod (){
if(myMap.containsKey("key1")) {
//todo...
} }
}
更多解釋
\\在 yaml 中用於將字符串分成多行
\\"是 yaml 字符串中 "(quote) 的轉義字符
yaml 中的{key:value} JSON 將被 @Value 轉換為 Map
#{ }是SpEL 表達式,可以在@Value 中使用,轉換json int Map 或Array / list 參考
在 Spring Boot 項目中測試
在直接 @Value 注入的情況下,最優雅的方法是將鍵值編寫為內聯 json(使用 ' 和 " 字符以避免繁瑣的轉義)並使用 SPEL 解析它:
#in yaml file:
my:
map:
is: '{ "key1":"val1",
"key2":"val2" }'
在你的@Component 或 @Bean 中:
@Component
public class MyClass{
@Value("#{${my.map.is}}")
Map<String,String> myYamlMap;
}
為了更方便的 YAML 語法,您可以完全避免使用 json 花括號,直接鍵入鍵值對
my:
map:
is: '"a":"b", "foo":"bar"'
並將缺少的花括號直接添加到您的 @Value SPEL 表達式中:
@Value("#{{${my.map.is}}}")
Map<String,String> myYamlMap;
該值將從 yaml 解析,包裝卷曲將連接到它,最后 SPEL 表達式將字符串解析為映射。
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
如果你想避免額外的結構,你可以讓它更簡單。
service:
mappings:
key1: value1
key2: value2
@Configuration
@EnableConfigurationProperties
public class ServiceConfigurationProperties {
@Bean
@ConfigurationProperties(prefix = "service.mappings")
public Map<String, String> serviceMappings() {
return new HashMap<>();
}
}
然后像往常一樣使用它,例如使用構造函數:
public class Foo {
private final Map<String, String> serviceMappings;
public Foo(Map<String, String> serviceMappings) {
this.serviceMappings = serviceMappings;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.