Est-il possible d'utiliser Spring @Value pour mapper les valeurs d'un fichier de propriétés vers HashMap.
Actuellement, j'ai quelque chose comme ça, et mapper une valeur n'est pas un problème. Mais je dois mapper les valeurs personnalisées dans les expirations HashMap. Est-ce que quelque chose comme ça est 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>();
Fichier de propriétés: 'my_service.properties'
service.cache=true
service.expiration.name1=100
service.expiration.name2=20
Est-il possible de mapper comme ceci: ensemble de valeurs
nom1 = 100
nom2 = 20
Je fais une solution inspirée par le post précédent.
Enregistrer le fichier de propriétés dans la configuration de printemps:
<util:properties id="myProp" location="classpath:my.properties"/>
Et je crée un composant:
@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;
}
}
Et lorsque je veux mapper toutes les propriétés commençant par une valeur spécifix vers HashMap, avec l'annotation @Value:
@Service
public class MyServiceImpl implements MyService {
@Value("#{PropertyMapper.startWith('myProp', 'service.expiration.', true)}")
private HashMap<String, Object> portalExpirations;
Est-il possible d'utiliser Spring @Value pour mapper les valeurs d'un fichier de propriétés vers HashMap?
Oui, ça l'est. Avec un peu d'aide de code et Spel .
Tout d'abord, considérons ce singleton Spring-bean (vous devriez le scanner):
@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);
}
}
Remarque: PropertySplitter
la classe utilise l'utilitaire Splitter
de Guava. Veuillez vous référer à sa documentation pour plus de détails.
Ensuite, dans votre haricot:
@Component
public class MyBean {
@Value("#{PropertySplitter.map('${service.expiration}')}")
Map<String, String> propertyAsMap;
}
Et enfin, la propriété:
service.expiration = name1:100,name2:20
Ce n’est pas exactement ce que vous avez demandé, car ce PropertySplitter
fonctionne avec une seule propriété transformée en un Map
, mais je pense que vous pouvez soit basculer à cette manière de spécifier les propriétés, ou modifiez le code PropertySplitter
afin qu'il corresponde à la manière plus hiérarchique que vous désirez.
Vous pouvez utiliser la syntaxe SPEL de type json pour écrire une mappe simple ou une mappe de liste dans un fichier de propriétés.
simple.map={'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}
map.of.list={\
'KEY1': {'value1','value2'}, \
'KEY2': {'value3','value4'}, \
'KEY3': {'value5'} \
}
J'ai utilisé \
pour une propriété multiligne améliorant la lisibilité
Ensuite, en Java, vous pouvez y accéder et l’analyser automatiquement avec @Value
comme ça.
@Value("#{${simple.map}}")
Map<String, String> simpleMap;
@Value("#{${map.of.list}}")
Map<String, List<String>> mapOfList;
Ci-joint ${simple.map}
, @Value
obtient la chaîne suivante du fichier de propriétés:
"{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}"
Ensuite, il est évalué comme s'il était en ligne
@Value("#{{'KEY1': 'value1', 'KEY2': 'value3', 'KEY3': 'value5'}}")
Vous pouvez en apprendre plus dans la documentation officielle
A partir de Spring 4.1.x (je ne me souviens pas de la version spécifique), vous pouvez faire quelque chose comme:
@Value("#{${your.properties.key.name}}")
private Map<String, String> myMap;
où votre.properties.key.name dans votre fichier de propriétés devrait être quelque chose comme
your.properties.key.name={\
name1 : 100, \
name2 : 200 \
}
Assurez-vous simplement que vous devez créer un bean PropertySourcesPlaceholderConfigurer pour que cela fonctionne dans votre application et si vous écrivez un code de test unitaire pour tester votre code, sinon un espace réservé $ {...} pour la valeur de la propriété ne fonctionnera pas comme prévu et vous verrez des erreurs SpringEL étranges.
La solution la plus rapide à base de printemps Boot à laquelle je peux penser suit. Dans mon exemple particulier, je migre des données d'un système à un autre. C'est pourquoi j'ai besoin d'un mapping pour un champ appelé priority.
J'ai d'abord créé le fichier de propriétés (priority-migration.properties) comme ceci:
my.prefix.priority.0:0
my.prefix.priority.10:1
my.prefix.priority.15:2
my.prefix.priority.20:2
another.prefix.foo:bar
et le mettre sur le classpath.
En supposant que vous souhaitiez utiliser la carte dans un composant/bean géré par un ressort, annotez votre classe avec:
@Component
@PropertySource("classpath:/priority-migration.properties")
Ce que vous voulez réellement dans votre carte, ce n’est bien sûr que les paires clé/valeur qui sont préfixées par mon.prefix, c’est-à-dire cette partie:
{
0:0
10:1
15:2
20:2
}
Pour cela, vous devez annoter votre composant avec
@ConfigurationProperties("my.prefix")
et créez un getter pour l'infixe priorité . Ce dernier s’est avéré être obligatoire dans mon cas (bien que Sring Doc indique qu’il suffit d’avoir une propriété prioritaire et d’initialiser avec une valeur mutable)
private final Map<Integer, Integer> priorityMap = new HashMap<>();
public Map<Integer, Integer> getPriority() {
return priorityMap;
}
À la fin
Cela ressemble à quelque chose comme ça:
@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
}
}