如何使用 Spring @Value 从 java 属性文件填充 HashMap

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28369458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 06:06:20  来源:igfitidea点击:

How to fill HashMap from java property file with Spring @Value

javaspringproperties-filespring-el

提问by d-sauer

Is it possible to use Spring @Value, to map values from properties file to the HashMap.

是否可以使用 Spring @Value 将值从属性文件映射到 HashMap。

Currently I have something like this, and mapping one value is not a problem. But I need to map custom values in HashMap expirations. Is something like this possible?

目前我有这样的东西,映射一个值不是问题。但我需要在 HashMap 到期时映射自定义值。这样的事情可能吗?

@Service
@PropertySource(value = "classpath:my_service.properties")
public class SomeServiceImpl implements SomeService {


    @Value("#{conf['service.cache']}")
    private final boolean useCache = false;

    @Value("#{conf['service.expiration.[<custom name>]']}")
    private final HashMap<String, String> expirations = new HashMap<String, String>();

Property file: 'my_service.properties'

属性文件:'my_service.properties'

service.cache=true
service.expiration.name1=100
service.expiration.name2=20

Is it posible to map like this key:value set

是否可以像这样映射键:值集

  • name1 = 100

  • name2 = 20

  • 名称 1 = 100

  • 名称 2 = 20

采纳答案by d-sauer

I make one solution inspired by the previous post.

我根据上一篇文章提出了一个解决方案。

Register property file in the Spring configuration:

在 Spring 配置中注册属性文件:

<util:properties id="myProp" location="classpath:my.properties"/>

And I create component:

我创建组件:

@Component("PropertyMapper")
public class PropertyMapper {

    @Autowired
    ApplicationContext applicationContext;

    public HashMap<String, Object> startWith(String qualifier, String startWith) {
        return startWith(qualifier, startWith, false);
    }

    public HashMap<String, Object> startWith(String qualifier, String startWith, boolean removeStartWith) {
        HashMap<String, Object> result = new HashMap<String, Object>();

        Object obj = applicationContext.getBean(qualifier);
        if (obj instanceof Properties) {
            Properties mobileProperties = (Properties)obj;

            if (mobileProperties != null) {
                for (Entry<Object, Object> e : mobileProperties.entrySet()) {
                    Object oKey = e.getKey();
                    if (oKey instanceof String) {
                        String key = (String)oKey;
                        if (((String) oKey).startsWith(startWith)) {
                            if (removeStartWith) 
                                key = key.substring(startWith.length());
                            result.put(key, e.getValue());
                        }
                    }
                }
            }
        }

        return result;
    }
}

And when I want to map all properties that begin with specifix value to HashMap, with @Value annotation:

当我想将所有以特定值开头的属性映射到 HashMap 时,使用 @Value 注释:

@Service
public class MyServiceImpl implements MyService {

    @Value("#{PropertyMapper.startWith('myProp', 'service.expiration.', true)}")
    private HashMap<String, Object> portalExpirations;

回答by Federico Peralta Schaffner

Is it possible to use Spring @Value, to map values from properties file to the HashMap?

是否可以使用 Spring @Value 将值从属性文件映射到 HashMap?

Yes, it is. With a little help of code and Spel.

是的。在代码和Spel 的帮助下。

Firstly, consider this singleton Spring-bean (you should scan it):

首先,考虑这个单例 Spring-bean(你应该扫描它):

@Component("PropertySplitter")
public class PropertySplitter {

    /**
     * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
     */
    public Map<String, String> map(String property) {
        return this.map(property, ",");
    }

    /**
     * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
     */
    public Map<String, List<String>> mapOfList(String property) {
        Map<String, String> map = this.map(property, ";");

        Map<String, List<String>> mapOfList = new HashMap<>();
        for (Entry<String, String> entry : map.entrySet()) {
            mapOfList.put(entry.getKey(), this.list(entry.getValue()));
        }

        return mapOfList;
    }

    /**
     * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
     */
    public List<String> list(String property) {
        return this.list(property, ",");
    }

    /**
     * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
     */
    public List<List<String>> groupedList(String property) {
        List<String> unGroupedList = this.list(property, ";");

        List<List<String>> groupedList = new ArrayList<>();
        for (String group : unGroupedList) {
            groupedList.add(this.list(group));
        }

        return groupedList;

    }

    private List<String> list(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
    }

    private Map<String, String> map(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
    }

}

Note:PropertySplitterclass uses Splitterutility from Guava. Please refer to its documentationfor further details.

注意:PropertySplitter类使用Splitter来自番石榴的实用程序。有关更多详细信息,请参阅其文档

Then, in some bean of yours:

然后,在你的一些豆子中:

@Component
public class MyBean {

    @Value("#{PropertySplitter.map('${service.expiration}')}")
    Map<String, String> propertyAsMap;

}

And finally, the property:

最后,属性:

service.expiration = name1:100,name2:20

It's not exactly what you've asked, because this PropertySplitterworks with one single property that is transformedinto a Map, but I think you could either switch to this way of specifying properties, or modify the PropertySplittercode so that it matches the more hierarchical way you desire.

这不完全是您所要求的,因为这PropertySplitter适用于转换为 a 的单个属性Map,但我认为您可以切换到这种指定属性的方式,或者修改PropertySplitter代码以使其与您希望的更分层的方式相匹配.

回答by Viktor Stoitschev

The quickest Spring Bootbased solution I can think of follows. In my particular example I am migrating data from one system to another. That is why I need a mapping for a field called priority.

我能想到的最快的基于Spring Boot的解决方案如下。在我的特定示例中,我将数据从一个系统迁移到另一个系统。这就是为什么我需要一个名为priority的字段的映射。

First I've created the properties file (priority-migration.properties) like such:

首先,我创建了这样的属性文件(priority-migration.properties):

my.prefix.priority.0:0
my.prefix.priority.10:1
my.prefix.priority.15:2
my.prefix.priority.20:2
another.prefix.foo:bar

and put it on the classpath.

并将其放在类路径上。

Assuming you want to use the map in a spring managed bean/component, annotate your class with:

假设您想在 spring 管理的 bean/组件中使用映射,请使用以下注释对您的类进行注释:

@Component
@PropertySource("classpath:/priority-migration.properties")

What you actually want in your map is of course only the key/value pairs which are prefixed with my.prefix, i.e. this part:

您在地图中真正想要的当然只是以 my.prefix 为前缀的键/值对,即这部分:

{
    0:0
    10:1
    15:2
    20:2
}

To achieve that you need to annotate your component with

要实现这一点,您需要使用

@ConfigurationProperties("my.prefix")

and create a getter for the priorityinfix. The latter proved to be mandatory in my case (although the Sring Docsays it is enough to have a property priorityand initialize it with a mutable value)

并为优先级中缀创建一个吸气剂。后者在我的情况下被证明是强制性的(尽管Sring Doc说拥有一个属性优先级并用可变值初始化它就足够了)

private final Map<Integer, Integer> priorityMap = new HashMap<>();

public Map<Integer, Integer> getPriority() {
    return priorityMap;
}


In the End

到底

It looks something like this:

它看起来像这样:

@Component
@ConfigurationProperties("my.prefix")
@PropertySource("classpath:/priority-migration.properties")
class PriorityProcessor {

    private final Map<Integer, Integer> priorityMap = new HashMap<>();

    public Map<Integer, Integer> getPriority() {
        return priorityMap;
    }

    public void process() {

        Integer myPriority = priorityMap.get(10)
        // use it here
    }
}

回答by wonhee

From Spring 4.1.x ( I can't remember specific version though ), you can do something like

从 Spring 4.1.x(虽然我不记得具体版本),你可以做类似的事情

@Value("#{${your.properties.key.name}}")
private Map<String, String> myMap;

where your.properties.key.name in your properties file should be something like

您的属性文件中的 your.properties.key.name 应该类似于

your.properties.key.name={\
    name1 : 100, \
    name2 : 200 \
}

Just make sure that you should create PropertySourcesPlaceholderConfigurer bean to make it work both in your app and if you are writing any unit test code to test your code, otherwise ${...} placeholder for the property value won't work as expected and you'll see some weird SpringEL errors.

只要确保您应该创建 PropertySourcesPlaceholderConfigurer bean 以使其在您的应用程序中工作,如果您正在编写任何单元测试代码来测试您的代码,否则属性值的 ${...} 占位符将无法按预期工作,并且你会看到一些奇怪的 SpringEL 错误。

回答by Marc Bouvier

You can use the SPEL json-like syntax to write a simple map or a map of list in property file.

您可以使用类似于 SPEL json 的语法在属性文件中编写简单的映射或列表映射。

simple.map={'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}

map.of.list={\
  'KEY1': {'value1','value2'}, \
  'KEY2': {'value3','value4'}, \
  'KEY3': {'value5'} \
 }

I used \for multiline property to enhance readability

我用于\多行属性以增强可读性

Then, in Java, you can access and parse it automatically with @Valuelike this.

然后,在 Java 中,您可以@Value像这样自动访问和解析它。

@Value("#{${simple.map}}")
Map<String, String> simpleMap;

@Value("#{${map.of.list}}")
Map<String, List<String>> mapOfList;

Here with ${simple.map}, @Valuegets the following String from the property file:

在这里${simple.map}@Value从属性文件中获取以下字符串:

"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"

Then, it is evaluated as if it was inlined

然后,它被评估为好像它是内联的

@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")

You can learn more in the official documentation

您可以在官方文档中了解更多信息

回答by Milan

Solution for pulling Mapusing @Valuefrom application.ymlproperty coded as multiline

使用@Valueapplication.yml属性中提取Map 的解决方案编码为多行

application.yml

应用程序.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 

Here the value for our map property "my-map-property-name" is stored in JSONformat inside a stringand we have achived multiline using \at end of line

在这里,我们的地图属性“my-map-property-name”的值以JSON格式存储在一个字符串中,我们在行尾使用\ 实现了多行

myJavaClass.java

我的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...
    } }

}

More explanation

更多解释

  • \in yaml it is Used to break string into multiline

  • \"is escape charater for "(quote) in yaml string

  • {key:value}JSON in yaml which will be converted to Map by @Value

  • #{ }it is SpEL expresion and can be used in @Value to convert json int Map or Array / list Reference

  • \在 yaml 中用于将字符串分成多行

  • \"是 yaml 字符串中 "(quote) 的转义字符

  • yaml 中的{key:value}JSON 将被 @Value 转换为 Map

  • #{ }是SpEL 表达式,可以在@Value 中使用,转换json int Map 或Array / list参考

Tested in a spring boot project

在 Spring Boot 项目中测试