如何使用@Value Spring注解注入Map?

105

如何使用Spring中的@Value注释从属性文件将值注入Map?

我的Spring Java类是,并尝试使用$,但出现以下错误消息:

无法自动装配字段:private java.util.Map Test.standard; 嵌套异常是java.lang.IllegalArgumentException:在字符串值“$ {com.test.standard}”中无法解析占位符“com.test.standard”

@ConfigurationProperty("com.hello.foo")
public class Test {

   @Value("${com.test.standard}")
   private Map<String,Pattern> standard = new LinkedHashMap<String,Pattern>

   private String enabled;

}

我有一个.properties文件,其中包含以下属性

com.test.standard.name1=Pattern1
com.test.standard.name2=Pattern2
com.test.standard.name3=Pattern3
com.hello.foo.enabled=true

你需要使用Spring表达式语言。有一个类似的问题使用了列表(https://dev59.com/k4Xca4cB1Zd3GeqPPOq0)。我不确定你是否可以直接实现你想要的功能。这个问题https://dev59.com/0F4c5IYBdhLWcg3wD2nC更贴近你的需求。它使用了自定义属性映射器。 - Laurentiu L.
你的地图中具体需要什么?看起来你还期望将某些类型转换为“Pattern”?那是什么样的“Pattern”类? - K Erlandsson
@Erlandsson 这是一个正则表达式模式,我们将在值中定义有效的正则表达式模式字符串。 - yathirigan
1
@LaurentuiL 在Spring Boot中,如果Map与类级别描述的前缀匹配,我可以直接注入Map,但是我的问题是类级别和属性级别的前缀不同。 - yathirigan
8个回答

193

您可以使用@Value注释从属性文件将值注入到Map中,如下所示。

属性文件中的属性。

propertyname={key1:'value1',key2:'value2',....}

在您的代码中。

@Value("#{${propertyname}}")  private Map<String,String> propertyname;

注意标签井号是注释的一部分。


12
如何设置默认值以防止属性缺失导致异常? - petertc
3
似乎还可以进行类型转换,例如:@Value("#{${double.map}}") final Map<String, Double> doubleMap - PeterK
8
如何在 yml 文件中指定相同的内容。 - Mukul Anand
6
@MukulAnand在YAML中的语法如下:propertyname : > { key1:'value', key2:'value' }抱歉,我无法正确格式化换行。 - joemat
9
“propertyname: {key1:'value1',key2:'value2',...}”模式在从.yaml文件中注入映射时无法使用:java.lang.IllegalArgumentException: 无法解析占位符 - Andrey M. Stepanov
显示剩余14条评论

24

我相信Spring Boot支持使用@ConfigurationProperties注解直接加载属性映射。

根据文档,你可以加载属性:

my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com

变成像这样的豆子:

@ConfigurationProperties(prefix="my")
public class Config {

    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

我以前使用过@ConfigurationProperties功能,但没有将其加载到映射中。你需要使用@EnableConfigurationProperties注释来启用此功能。

这个特性很酷的一点是你可以验证你的属性


是的,但是我的问题是.. Test类有它自己的@ConfigurationProperties前缀。因此,我想仅为这个成员变量使用不同的前缀。我该怎么办? - yathirigan
1
嗯,我漏了那个。所以我会使用@ConfiguraitonProperties注解创建两个单独的bean,并将它们自动装配到测试类中。 - luboskrnac
可能适用于问题发起者,但问题并没有指定boot,而且这个问题在没有boot的情况下对于一般的Spring是不工作的。 - xenoterracide
13
问题是如何使用@Value注释向地图中注入内容,但你提到了许多其他事情,而没有直接回答问题。提供替代方案可以,但请确保也回答了问题。 - Mukul Anand

21

我有一段简单的Spring Cloud Config代码,就像这样:

在application.properties中:

spring.data.mongodb.db1=mongodb://test@test1.com

spring.data.mongodb.db2=mongodb://test@test2.com

读取

@Bean(name = "mongoConfig")
@ConfigurationProperties(prefix = "spring.data.mongodb")
public Map<String, Map<String, String>> mongoConfig() {
    return new HashMap();
}

使用

@Autowired
@Qualifier(value = "mongoConfig")
private Map<String, String> mongoConfig;

@Bean(name = "mongoTemplates")
public HashMap<String, MongoTemplate> mongoTemplateMap() throws UnknownHostException {
    HashMap<String, MongoTemplate> mongoTemplates = new HashMap<>();
    for (Map.Entry<String, String>> entry : mongoConfig.entrySet()) {
        String k = entry.getKey();
        String v = entry.getValue();
        MongoTemplate template = new MongoTemplate(new SimpleMongoDbFactory(new MongoClientURI(v)));
        mongoTemplates.put(k, template);
    }
    return mongoTemplates;
}

1
我认为你对bean mongoConfig的定义是错误的。这个方法应该像这样定义: public Map<String, String> mongoConfig() { return new HashMap(); } - rslj
如果您只想从application.yml注入一个map,那么这可能是最优雅的方法。 - Agoston Horvath

20
你可以使用 @Resource 注释,在你的类中将 .properties 注入为一个映射。如果你正在使用基于XML 的配置,那么请在你的Spring配置文件中添加以下bean:
 <bean id="myProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
      <property name="location" value="classpath:your.properties"/>
 </bean>

针对注解的:

@Bean(name = "myProperties")
public static PropertiesFactoryBean mapper() {
        PropertiesFactoryBean bean = new PropertiesFactoryBean();
        bean.setLocation(new ClassPathResource(
                "your.properties"));
        return bean;
}

然后您可以在应用程序中将它们作为Map获取:

@Resource(name = "myProperties")
private Map<String, String> myProperties;

我们使用了Spring Cloud Config服务器来提供配置,因此类路径方法可能不起作用。而且我们不使用XML文件。 - yathirigan
@Arpit - 你能在这里给我指导一下吗:https://stackoverflow.com/questions/60899860/spring-spel-expression-language-to-create-map-of-string-and-custom-object? - PAA

17

以下方法适用于我:

SpringBoot 2.1.7.RELEASE

YAML属性(请注意,值要用单引号括起来)

property:
   name: '{"key1": false, "key2": false, "key3": true}'

在Java/Kotlin中使用#来注释字段(对于Java不需要用\转义$)

@Value("#{\${property.name}}")

如何在找不到属性时正确地回退到空映射?否则上下文将无法初始化。 - Simon Logic
它适用于application.yml,可以正常工作。 - 袁文涛

16
要使其与YAML配合工作,请按照以下步骤操作:
property-name: '{
  key1: "value1",
  key2: "value2"
}'

7
以下是我们的做法。 两个示例类如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
@EnableKafka
@Configuration
@EnableConfigurationProperties(KafkaConsumerProperties.class)
public class KafkaContainerConfig {

    @Autowired
    protected KafkaConsumerProperties kafkaConsumerProperties;

    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        return new DefaultKafkaConsumerFactory<>(kafkaConsumerProperties.getKafkaConsumerConfig());
    }
...

@Configuration
@ConfigurationProperties
public class KafkaConsumerProperties {
    protected Map<String, Object> kafkaConsumerConfig = new HashMap<>();

    @ConfigurationProperties("kafkaConsumerConfig")
    public Map<String, Object> getKafkaConsumerConfig() {
        return (kafkaConsumerConfig);
    }
...

从属性文件提供kafkaConsumer配置,您可以使用:mapname[key]=value。
//application.properties
kafkaConsumerConfig[bootstrap.servers]=localhost:9092, localhost:9093, localhost:9094
kafkaConsumerConfig[group.id]=test-consumer-group-local
kafkaConsumerConfig[value.deserializer]=org.apache.kafka.common.serialization.StringDeserializer
kafkaConsumerConfig[key.deserializer=org.apache.kafka.common.serialization.StringDeserializer

为了从yaml文件中提供kafkaConsumer配置,您可以使用"[key]": value 在application.yml文件中:
kafkaConsumerConfig:
  "[bootstrap.servers]": localhost:9092, localhost:9093, localhost:9094
  "[group.id]": test-consumer-group-local
  "[value.deserializer]": org.apache.kafka.common.serialization.StringDeserializer
  "[key.deserializer]": org.apache.kafka.common.serialization.StringDeserializer

3
您可以使用以下代码。 application.yml的下面代码:
my:
  mapValues:
    dbData: '{
      "connectionURL": "http://tesst:3306",
        "userName": "myUser",
        "password": "password123"
    }'

使用以下Java代码,可以使用@Value注释访问这些键和值。

@Value("#{${my.mapValues.dbData}}")
private Map<String,String> dbValues;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接