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

是否可以使用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.[]']}") private final HashMap expirations = new HashMap(); 

属性文件:’my_service.properties’

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

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

  • name1 = 100

  • name2 = 20

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

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

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

 @Component("PropertySplitter") public class PropertySplitter { /** * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2 */ public Map map(String property) { return this.map(property, ","); } /** * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2 */ public Map> mapOfList(String property) { Map map = this.map(property, ";"); Map> mapOfList = new HashMap<>(); for (Entry entry : map.entrySet()) { mapOfList.put(entry.getKey(), this.list(entry.getValue())); } return mapOfList; } /** * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4 */ public List list(String property) { return this.list(property, ","); } /** * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2 */ public List> groupedList(String property) { List unGroupedList = this.list(property, ";"); List> groupedList = new ArrayList<>(); for (String group : unGroupedList) { groupedList.add(this.list(group)); } return groupedList; } private List list(String property, String splitter) { return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property); } private Map map(String property, String splitter) { return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property); } } 

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

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

 @Component public class MyBean { @Value("#{PropertySplitter.map('${service.expiration}')}") Map propertyAsMap; } 

最后,财产:

 service.expiration = name1:100,name2:20 

它并不完全是你所问的,因为这个PropertySplitter可以处理一个转换Map单个属性,但我认为你可以切换到这种指定属性的方式,或者修改PropertySplitter代码以使它匹配更多的层次结构你渴望的方式。

我从上一篇文章中获得灵感。

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

  

我创建组件:

 @Component("PropertyMapper") public class PropertyMapper { @Autowired ApplicationContext applicationContext; public HashMap startWith(String qualifier, String startWith) { return startWith(qualifier, startWith, false); } public HashMap startWith(String qualifier, String startWith, boolean removeStartWith) { HashMap result = new HashMap(); Object obj = applicationContext.getBean(qualifier); if (obj instanceof Properties) { Properties mobileProperties = (Properties)obj; if (mobileProperties != null) { for (Entry 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; } } 

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

 @Service public class MyServiceImpl implements MyService { @Value("#{PropertyMapper.startWith('myProp', 'service.expiration.', true)}") private HashMap portalExpirations; 

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

首先,我创建了属性文件(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 

并把它放在类路径上。

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

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

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

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

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

 @ConfigurationProperties("my.prefix") 

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

 private final Map priorityMap = new HashMap<>(); public Map getPriority() { return priorityMap; } 

到底

它看起来像这样:

 @Component @ConfigurationProperties("my.prefix") @PropertySource("classpath:/priority-migration.properties") class PriorityProcessor { private final Map priorityMap = new HashMap<>(); public Map getPriority() { return priorityMap; } public void process() { Integer myPriority = priorityMap.get(10) // use it here } } 

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

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

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

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

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

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

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

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

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

 @Value("#{${simple.map}}") Map simpleMap; @Value("#{${map.of.list}}") Map> mapOfList; 

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

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

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

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

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