Cómo rellenar HashMap desde el archivo de propiedades de Java con Spring @Value

¿Es posible utilizar Spring @Value para mapear valores del archivo de propiedades al HashMap?

Actualmente tengo algo como esto, y mapear un valor no es un problema. Pero necesito asignar valores personalizados en expiraciones de HashMap. Es algo como esto posible?

@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(); 

Archivo de propiedad: ‘my_service.properties’

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

¿Es posible mapear como esta clave: conjunto de valores

  • name1 = 100

  • name2 = 20

¿Es posible usar Spring @Value para mapear valores del archivo de propiedades al HashMap?

Sí lo es. Con un poco de ayuda de código y Spel .

En primer lugar, considere este singleton Spring-bean (debe escanearlo):

 @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); } } 

Nota: PropertySplitter clase PropertySplitter usa la utilidad Splitter de Guava. Por favor, consulte su documentación para más detalles.

Entonces, en un frijol tuyo:

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

Y finalmente, la propiedad:

 service.expiration = name1:100,name2:20 

No es exactamente lo que ha preguntado, porque este PropertySplitter funciona con una sola propiedad que se transforma en un Map , pero creo que podría cambiar a esta manera de especificar propiedades o modificar el código PropertySplitter para que coincida con el más jerárquico camino que deseas.

Hago una solución inspirada en la publicación anterior.

Registrar el archivo de propiedad en la configuración de Spring:

  

Y creo componentes:

 @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; } } 

Y cuando quiero mapear todas las propiedades que comienzan con el valor de specifix en HashMap, con la anotación @Value:

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

La solución más rápida basada en Spring Boot que se me ocurre es la siguiente. En mi ejemplo particular estoy migrando datos de un sistema a otro. Es por eso que necesito un mapeo para un campo llamado prioridad .

Primero he creado el archivo de propiedades (priority-migration.properties) como este:

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

y ponerlo en el classpath.

Suponiendo que desea utilizar el mapa en un bean / componente manejado por resorte, anote su clase con:

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

Lo que realmente quiere en su mapa es, por supuesto, solo los pares clave / valor que están prefijados con my.prefix, es decir, esta parte:

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

Para lograr eso, debe anotar su componente con

 @ConfigurationProperties("my.prefix") 

y crea un getter para el infijo prioritario . Este último resultó ser obligatorio en mi caso (aunque el documento de Sring dice que es suficiente tener una prioridad de propiedad e inicializarlo con un valor variable)

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

En el final

Se ve algo como esto:

 @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 } } 

Desde Spring 4.1.x (aunque no recuerdo la versión específica), puedes hacer algo como

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

donde your.properties.key.name en su archivo de propiedades debería ser algo así como

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

Solo asegúrese de crear el bean PropertySourcesPlaceholderConfigurer para que funcione tanto en su aplicación como si está escribiendo un código de prueba unitaria para probar el código; de lo contrario, $ {…} marcador de posición para el valor de la propiedad no funcionará como se espera y verás algunos errores SpringEL extraños.