Lecture d'une liste à partir du fichier de propriétés et chargement avec annotation de ressort @Value


244

Je veux avoir une liste de valeurs dans un fichier .properties, c'est-à-dire:

my.list.of.strings=ABC,CDE,EFG

Et pour le charger directement dans ma classe, c'est à dire:

@Value("${my.list.of.strings}")
private List<String> myList;

Si je comprends bien, une alternative consiste à l'avoir dans le fichier de configuration de printemps et à le charger comme référence de bean (corrigez-moi si je me trompe), c'est-à-dire

<bean name="list">
 <list>
  <value>ABC</value>
  <value>CDE</value>
  <value>EFG</value>
 </list>
</bean>

Mais existe-t-il un moyen de le faire? en utilisant un fichier .properties? ps: je voudrais le faire sans code personnalisé si possible.

Réponses:


454

Utilisation de Spring EL:

@Value("#{'${my.list.of.strings}'.split(',')}") 
private List<String> myList;

En supposant que votre fichier de propriétés est correctement chargé avec les éléments suivants:

my.list.of.strings=ABC,CDE,EFG

1
Je l'ai vérifié à nouveau en utilisant la même version que vous utilisez. Copié le Spring EL exactement comme dans le message et cela fonctionne. Ce qui est différent cependant, c'est que si je fais une erreur dans mon EL, j'obtiens une org.springframework.expression.spel.SpelEvaluationExceptionexception et non javax.el.ELException. Votre objection est-elle créée par Spring?
Wilhelm Kleu

1
Fonctionne parfaitement avec Spring 3.2.
ehsanullahjan

17
Que diriez-vous d'une propriété vide my.list.of.strings=? Je m'attendrais à ce qu'une telle fonctionnalité redéfinisse la liste vide où ici ce sera un élément (chaîne vide), n'est-ce pas?
Jan Zyka

5
Notez également que la solution suggérée ne fait pas de découpage, donc des valeurs comme item1, item2, item3peuvent vous donner un résultat auquel vous ne vous attendez pas vraiment (indice: espaces).
Jan Zyka,

4
Si vous avez besoin de réduire les espaces vides, utilisez l'expression régulière pour l'argument divisé .. quelque chose comme@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007

89

Depuis Spring 3.0, vous pouvez ajouter une ligne comme

<bean id="conversionService" 
    class="org.springframework.context.support.ConversionServiceFactoryBean" />

à ton applicationContext.xml (ou où vous configurez les choses). Comme le souligne Dmitry Chornyi dans un commentaire, la configuration basée sur Java ressemble à:

@Bean public ConversionService conversionService() {
    return new DefaultConversionService();
}

Cela active le nouveau service de configuration qui prend en charge la conversion String en Collectiontypes. Si vous n'activez pas ce service de configuration, Spring se rabat sur ses éditeurs de propriétés hérités en tant que services de configuration, qui ne prennent pas en charge ce type de conversion.

La conversion en collections d'autres types fonctionne également:

@Value("${my.list.of.ints}")
private List<Integer> myList

fonctionnera avec une ligne comme

 my.list.of.ints= 1, 2, 3, 4

Aucun problème avec les espaces blancs là-bas, le ConversionServiceFactoryBeanprend en charge.

Voir http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#core-convert-Spring-config

Dans une application Spring, vous configurez généralement une instance ConversionService par conteneur Spring (ou ApplicationContext). Ce ConversionService sera récupéré par Spring, puis utilisé chaque fois qu'une conversion de type doit être effectuée par le framework. [...] Si aucun ConversionService n'est enregistré auprès de Spring, le système d'origine basé sur PropertyEditor est utilisé.


6
Configuration Java: @Bean public ConversionService conversionService () {return new DefaultConversionService (); }
Dmitry Chornyi

2
En dehors en évitant de vous répéter avec split()dans chaque expression, il gère également correctement une liste vide au lieu de vous donner[null]
Didier L

Cela ne fonctionne pas si la propriété est remplacée (existe dans plusieurs sources de propriété.)
Daniel Hári

47

Si vous lisez ceci et que vous utilisez Spring Boot , vous avez 1 option de plus pour cette fonctionnalité

Habituellement, la liste séparée par des virgules est très maladroite pour un cas d'utilisation réel (et parfois même impossible, si vous souhaitez utiliser des virgules dans votre configuration):

email.sendTo=somebody@example.com,somebody2@example.com,somebody3@example.com,.....

Avec Spring Boot , vous pouvez l'écrire comme ceci (l'index commence à 0):

email.sendTo[0]=somebody@example.com
email.sendTo[1]=somebody2@example.com
email.sendTo[2]=somebody3@example.com

Et utilisez-le comme ceci:

@Component
@ConfigurationProperties("email")
public class EmailProperties {

    private List<String> sendTo;

    public List<String> getSendTo() {
        return sendTo;
    }

    public void setSendTo(List<String> sendTo) {
        this.sendTo = sendTo;
    }

}


@Component
public class EmailModel {

  @Autowired
  private EmailProperties emailProperties;

  //Use the sendTo List by 
  //emailProperties.getSendTo()

}



@Configuration
public class YourConfiguration {
    @Bean
  public EmailProperties emailProperties(){
        return new EmailProperties();
  }

}


#Put this in src/main/resource/META-INF/spring.factories
  org.springframework.boot.autoconfigure.EnableAutoConfiguration=example.compackage.YourConfiguration

J'ai un problème où les autres solutions décrites ici ne fonctionnent pas car les virgules échappées ne sont pas reconnues. Existe-t-il un moyen de faire cette solution avec Spring 4?
sandrozbinden

34

En spécifiant le my.list.of.strings=ABC,CDE,EFGfichier in .properties et en utilisant

@Value("${my.list.of.strings}") private String[] myString;

Vous pouvez obtenir les tableaux de chaînes. Et en utilisant CollectionUtils.addAll(myList, myString), vous pouvez obtenir la liste des chaînes.


Je ne reçois que la première chaîne "ABC"
Osama Abdulsattar

19

Avez-vous envisagé de @Autowiredconstruire un constructeur ou un poseur et de vous String.split()plonger dans le corps?

class MyClass {
    private List<String> myList;

    @Autowired
    public MyClass(@Value("${my.list.of.strings}") final String strs) {
        myList = Arrays.asList(strs.split(","));
    }

    //or

    @Autowired
    public void setMyList(@Value("${my.list.of.strings}") final String strs) {
        myList = Arrays.asList(strs.split(","));
    }
}

J'ai tendance à préférer effectuer mon câblage automatique de l'une de ces façons pour améliorer la testabilité de mon code.


14

Si vous utilisez Spring Boot 2, il fonctionne tel quel, sans configuration supplémentaire.

my.list.of.strings=ABC,CDE,EFG

@Value("${my.list.of.strings}")
private List<String> myList;

n'a pas fonctionné pour moi, je dois utiliser le Spring EL comme indiqué ci-dessus.
Bilbo Baggins

ou mêmeprivate List<String> myList;
Halayem Anis

Oui, cela a aussi fonctionné pour moi: j'utilise Spring Boot 2.2.6
Ankit

8

Toutes les réponses ci-dessus sont correctes. Mais vous pouvez y parvenir en une seule ligne. Veuillez essayer la déclaration suivante et vous obtiendrez toutes les valeurs séparées par des virgules dans une liste de chaînes.

private @Value("#{T(java.util.Arrays).asList(projectProperties['my.list.of.strings'])}") List<String> myList;

Et vous devez également avoir la ligne suivante définie dans votre configuration xml.

<util:properties id="projectProperties" location="/project.properties"/>

remplacez simplement le chemin et le nom de fichier de votre fichier de propriétés. Et vous êtes prêt à partir. :)

J'espère que cela vous aidera. À votre santé.


1
Cela a fonctionné pour moi, mais j'ai dû formuler l'annotation comme ceci:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven

6

Si vous utilisez la dernière version du framework Spring (Spring 3.1+ je crois), vous n'avez pas besoin de ces éléments de séparation de chaînes dans SpringEL,

Ajoutez simplement PropertySourcesPlaceholderConfigurer et DefaultConversionService dans la classe de configuration de votre Spring (celle avec annotée avec Configuration), par exemple,

@Configuration
public class AppConfiguration {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean public ConversionService conversionService() {
        return new DefaultConversionService();
    }
}

et dans ta classe

@Value("${list}")
private List<String> list;

et dans le fichier de propriétés

list=A,B,C,D,E

Sans DefaultConversionService, vous ne pouvez prendre une chaîne séparée par des virgules dans un tableau de chaînes que lorsque vous injectez la valeur dans votre champ, mais DefaultConversionService fait quelques magie pratique pour vous et les ajoutera à Collection, Array, etc. (vérifiez l'implémentation si vous le souhaitez) j'aimerais en savoir plus)

Avec ces deux, il gère même tous les espaces blancs redondants, y compris la nouvelle ligne, vous n'avez donc pas besoin d'ajouter de logiques supplémentaires pour les couper.


L'ajout de cette configuration fonctionne mais quelque chose d'étrange se produit: je ne peux pas utiliser @Value pour le type de fichier, je dois changer File with Resource.
ad3luc

2

vous pouvez le faire avec des annotations comme celle-ci

 @Value("#{T(java.util.Arrays).asList('${my.list.of.strings:a,b,c}')}") 
    private List<String> mylist;

ici my.list.of.strings sera choisi dans le fichier de propriétés, s'il n'est pas là, alors les valeurs par défaut a, b, c seront utilisées

et dans votre fichier de propriétés, vous pouvez avoir quelque chose comme ça

my.list.of.strings = d, e, f


2

Méfiez-vous des espaces dans les valeurs. Je peux me tromper, mais je pense que les espaces dans la liste séparée par des virgules ne sont pas tronqués en utilisant @Value et Spel. La liste

foobar=a, b, c

serait lu comme une liste de chaînes

"a", " b", " c"

Dans la plupart des cas, vous ne voudrez probablement pas les espaces!

L'expression

@Value("#{'${foobar}'.trim().replaceAll(\"\\s*(?=,)|(?<=,)\\s*\", \"\").split(',')}")
private List<String> foobarList;

vous donnerait une liste de chaînes:

"a", "b", "c".

L'expression régulière supprime tous les espaces juste avant et juste après une virgule. Les espaces à l'intérieur des valeurs ne sont pas supprimés. Alors

foobar = AA, B B, CCC

devrait entraîner des valeurs

"AA", "B B", "CCC".

La méthode split () utilise des regex internes comme délimiteur, donc votre exemple peut être simplifié: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B

2

Je pense que c'est plus simple pour saisir le tableau et supprimer les espaces:

@Value("#{'${my.array}'.replace(' ', '').split(',')}")
private List<String> array;

2

Dans mon cas d'une liste d'entiers fonctionne ceci:

@Value("#{${my.list.of.integers}}")
private List<Integer> listOfIntegers;

Fichier de propriétés:

my.list.of.integers={100,200,300,400,999}

ou @Value ("# {$ {my.set.of.integers}}") private Set <Integer> setOfIntegers;
Alexey Simonov

1

Pensez à utiliser la configuration Commons. Il a intégré une fonctionnalité pour casser une entrée du fichier de propriétés dans un tableau / liste. Combiner avec SpEL et @Value devrait donner ce que vous voulez


Comme demandé, voici ce dont vous avez besoin (je n'ai pas vraiment essayé le code, j'ai peut-être quelques fautes de frappe, merci de me le faire):

Dans la configuration Apache Commons, il y a PropertiesConfiguration. Il prend en charge la fonction de conversion de chaîne délimitée en tableau / liste.

Par exemple, si vous avez un fichier de propriétés

#Foo.properties
foo=bar1, bar2, bar3

Avec le code ci-dessous:

PropertiesConfiguration config = new PropertiesConfiguration("Foo.properties");
String[] values = config.getStringArray("foo");

vous donnera un tableau de chaînes de ["bar1", "bar2", "bar3"]

Pour l'utiliser avec Spring, ayez ceci dans votre contexte d'application xml:

<bean id="fooConfig" class="org.apache.commons.configuration.PropertiesConfiguration">
    <constructor-arg type="java.lang.String" value="classpath:/Foo.properties"/>
</bean>

et avoir ceci dans votre haricot de printemps:

public class SomeBean {

    @Value("fooConfig.getStringArray('foo')")
    private String[] fooArray;
}

Je crois que cela devrait fonctionner: P


pourriez-vous être plus précis sur la méthode et la classe à utiliser, et un exemple de sniplet de code réel serait formidable.
JackDev

1

Ma façon préférée (pour les chaînes, en particulier), est la suivante:

admin.user={'Doe, John','Headroom, Max','Mouse, Micky'}

et utilise

@Value("#{${admin.user}}")
private List<String> userList;

De cette façon, vous pouvez également inclure des virgules dans votre paramètre. Cela fonctionne également pour les ensembles.


0

si vous utilisez des espaces réservés de propriété, l'exemple ser1702544 deviendrait

@Value("#{myConfigProperties['myproperty'].trim().replaceAll(\"\\s*(?=,)|(?<=,)\\s*\", \"\").split(',')}") 

Avec l'espace réservé xml:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">   
    <property name="properties" ref="myConfigProperties" />
    <property name="placeholderPrefix"><value>$myConfigProperties{</value></property>
</bean>    

<bean id="myConfigProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
     <property name="locations">
         <list>
                <value>classpath:myprops.properties</value>
         </list>
     </property>
</bean> 

0

J'utilise Spring Boot 2.2.6

Mon dossier de propriété:

usa.big.banks= JP Morgan, Wells Fargo, Citigroup, Morgan Stanley, Goldman Sachs

Mon code:

@Value("${usa.big.banks}")
    private List<String> bigBanks;

@RequestMapping("/bigbanks")
    public String getBanks() {
        System.out.println("bigBanks = " + bigBanks);
        return bigBanks.toString();
    }

Ça fonctionne bien

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.