简体   繁体   English

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

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

Is it possible to use Spring @Value, to map values from properties file to the HashMap.是否可以使用 Spring @Value,从属性文件到 map 值到 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.但我需要在 HashMap 到期时使用 map 自定义值。 Is something like this possible?这样的事情可能吗?

@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是否可以像这样设置 map 键:值集

  • name1 = 100名称 1 = 100

  • name2 = 20名称 2 = 20

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 @Value like this.然后,在 Java 中,您可以像这样使用@Value自动访问和解析它。

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

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

Here with ${simple.map} , @Value gets 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您可以在官方文档中了解更多信息

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: PropertySplitter class uses Splitter utility from Guava.注意: PropertySplitter类使用来自 Guava 的Splitter实用程序。 Please refer toits documentation for further details.有关更多详细信息,请参阅其文档

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 PropertySplitter works with one single property that is transformed into a Map , but I think you could either switch to this way of specifying properties, or modify the PropertySplitter code so that it matches the more hierarchical way you desire.这并不完全是您所问的,因为这个PropertySplitter使用一个转换Map单个属性,但我认为您可以切换到这种指定属性的方式,或者修改PropertySplitter代码,使其与更多层次结构相匹配你想要的方式。

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 错误。

The quickest Spring Boot based solution I can think of follows.我能想到的最快的基于 Spring Boot的解决方案如下。 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 .这就是为什么我需要一个名为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, ie 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 priority infix.并为优先级中缀创建一个吸气剂。 The latter proved to be mandatory in my case (although the Sring Doc says it is enough to have a property priority and 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
    }
}

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;

Solution for pulling Map using @Value from application.yml property 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 JSON format inside a string and 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 \\在 yaml 中用于将字符串分成多行

  • \\" is escape charater for "(quote) in yaml string \\"是 yaml 字符串中 "(quote) 的转义字符

  • {key:value} JSON in yaml which will be converted to Map by @Value yaml 中的{key:value} JSON 将被 @Value 转换为 Map

  • #{ } it is SpEL expresion and can be used in @Value to convert json int Map or Array / list Reference #{ }是SpEL 表达式,可以在@Value 中使用,转换json int Map 或Array / list 参考

Tested in a spring boot project在 Spring Boot 项目中测试

Use the same variable name as the Yaml name使用与 Yaml 名称相同的变量名称

Eg:例如:

private final HashMap<String, String> expiration 

instead of代替

private final HashMap<String, String> expirations 

Or something similar to this in properties file或者属性文件中类似的东西

org.code=0009,0008,0010
org.code.0009.channel=30,40
org.code.0008.channel=30,40
org.code.0010.channel=30,40

in Java, read org.code and then loop thru each org.code and build org.code..channel and put it into a map....在 Java 中,读取 org.code 然后遍历每个 org.code 并构建 org.code..channel 并将其放入 map....

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM