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

d-s*_*uer 36 java spring spring-el properties-file

是否可以使用Spring @Value将属性文件中的值映射到HashMap.

目前我有类似的东西,映射一个值不是问题.但我需要在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>();
Run Code Online (Sandbox Code Playgroud)

属性文件:'my_service.properties'

service.cache=true
service.expiration.name1=100
service.expiration.name2=20
Run Code Online (Sandbox Code Playgroud)

像这个键映射是否可行:值集

  • name1 = 100

  • name2 = 20

Fed*_*ner 22

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

是的.借助代码和Spel的一点帮助.

首先,考虑一下这个单例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);
    }

}
Run Code Online (Sandbox Code Playgroud)

注意: PropertySplitter class使用SplitterGuava的实用程序.有关更多详细信息,请参阅其文档.

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

@Component
public class MyBean {

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

}
Run Code Online (Sandbox Code Playgroud)

最后,财产:

service.expiration = name1:100,name2:20
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*ier 17

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

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

map.of.list={\
  'KEY1': {'value1','value2'}, \
  'KEY2': {'value3','value4'}, \
  'KEY3': {'value5'} \
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"
Run Code Online (Sandbox Code Playgroud)

然后,它被评估为内联

@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")
Run Code Online (Sandbox Code Playgroud)

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


d-s*_*uer 15

我的灵感来自前一篇文章的一个解决方案.

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

<util:properties id="myProp" location="classpath:my.properties"/>
Run Code Online (Sandbox Code Playgroud)

我创建组件:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@Service
public class MyServiceImpl implements MyService {

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


won*_*hee 11

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

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

你的属性文件中的your.properties.key.name应该是这样的

your.properties.key.name={\
    name1 : 100, \
    name2 : 200 \
}
Run Code Online (Sandbox Code Playgroud)

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

  • 加一是解决问题的最简单、最简单的新方法 (3认同)

Vik*_*hev 7

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

首先,我创建了属性文件(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
Run Code Online (Sandbox Code Playgroud)

并把它放在类路径上.

假设您要在spring托管bean /组件中使用该映射,请使用以下内容为您的类添加注释:

@Component
@PropertySource("classpath:/priority-migration.properties")
Run Code Online (Sandbox Code Playgroud)

你在地图中实际需要的只是以my.prefix为前缀的键/值对,即这部分:

{
    0:0
    10:1
    15:2
    20:2
}
Run Code Online (Sandbox Code Playgroud)

要实现这一点,您需要使用注释组件

@ConfigurationProperties("my.prefix")
Run Code Online (Sandbox Code Playgroud)

并为优先级中缀创建一个getter .后者在我的案例中证明是强制性的(尽管Sring Doc说它足以拥有属性优先级并用可变值初始化它)

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

public Map<Integer, Integer> getPriority() {
    return priorityMap;
}
Run Code Online (Sandbox Code Playgroud)

到底

它看起来像这样:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @ConfigurationProperties是Spring Boot注释,不是Spring注释 (2认同)