Leggere un elenco dal file delle proprietà e caricarlo con l'annotazione a molla @Value


244

Voglio avere un elenco di valori in un file .properties, ovvero:

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

E per caricarlo direttamente nella mia classe, cioè:

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

A quanto ho capito, un'alternativa a farlo è di averlo nel file di configurazione di primavera e caricarlo come riferimento bean (correggimi se sbaglio), cioè

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

Ma c'è un modo per farlo? utilizzando un file .properties? ps: vorrei farlo senza alcun codice personalizzato, se possibile.

Risposte:


454

Utilizzando Spring EL:

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

Supponendo che il file delle proprietà sia caricato correttamente con quanto segue:

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

1
L'ho ricontrollato usando la stessa versione che stai usando. Ho copiato Spring EL esattamente come nel post e funziona. Ciò che è diverso è che se commetto un errore nel mio EL ottengo org.springframework.expression.spel.SpelEvaluationExceptionun'eccezione e non javax.el.ELException. Il tuo oggetto è stato creato da Spring?
Wilhelm Kleu,

1
Funziona perfettamente con Spring 3.2.
ehsanullahjan

17
Che ne dici di proprietà vuote my.list.of.strings=? Mi aspetterei che tale funzionalità ricalchi la lista vuota in cui qui ci sarà un elemento (stringa vuota), no?
Jan Zyka,

5
Si noti inoltre che la soluzione suggerita non esegue il taglio, quindi valori come item1, item2, item3potrebbero darti risultati che non ti aspetti (suggerimento: spazi).
Jan Zyka,

4
Se hai bisogno di tagliare gli spazi vuoti usa regex per argomento diviso ... qualcosa del genere@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007

88

Dalla primavera 3.0, puoi aggiungere una riga come

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

al tuo applicationContext.xml(o dove configuri le cose). Come sottolinea Dmitry Chornyi in un commento, la configurazione basata su Java assomiglia a:

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

Ciò attiva il nuovo servizio di configurazione che supporta la conversione Stringin Collectiontipi. Se non si attiva questo servizio di configurazione, Spring ricorre ai suoi editor di proprietà legacy come servizi di configurazione, che non supportano questo tipo di conversione.

Anche la conversione in raccolte di altri tipi funziona:

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

funzionerà con una linea come

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

Nessun problema con gli spazi bianchi lì, se ne ConversionServiceFactoryBeanprende cura.

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

In un'applicazione Spring, in genere si configura un'istanza ConversionService per container Spring (o ApplicationContext). Tale servizio di conversione verrà raccolto da Spring e quindi utilizzato ogni volta che una conversione di tipo deve essere eseguita dal framework. [...] Se nessun servizio di conversione è registrato con Spring, viene utilizzato il sistema originale basato su PropertyEditor.


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

2
Al di fuori evitando di ripetere te stesso con split()ogni espressione, gestisce anche correttamente un elenco vuoto invece di darti[null]
Didier L

Non funziona se la proprietà viene ignorata (esiste in più fonti di proprietà.)
Daniel Hári,

47

Se stai leggendo questo e stai usando Spring Boot , hai 1 opzione in più per questa funzione

Di solito l'elenco separato da virgole è molto goffo per il caso d'uso del mondo reale (E a ​​volte non è nemmeno possibile, se si desidera utilizzare le virgole nella propria configurazione):

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

Con Spring Boot , puoi scriverlo in questo modo (Index inizia da 0):

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

E usalo come questi:

@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

Ho un problema a causa del quale le altre soluzioni qui descritte non funzionano a causa di virgole sfuggite non riconosciute. C'è un modo per fare questa soluzione con Spring 4?
sandrozbinden,

34

Specificando il my.list.of.strings=ABC,CDE,EFGfile in .properties e usando

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

Puoi ottenere gli array di stringhe. E usando CollectionUtils.addAll(myList, myString), puoi ottenere l'elenco delle stringhe.


Ricevo solo la prima stringa "ABC"
Osama Abdulsattar l'

19

Hai considerato @Autowiredl'ingegnere o il setter e l' String.split()ing nel corpo?

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(","));
    }
}

Tendo a preferire il mio autowiring in uno di questi modi per migliorare la testabilità del mio codice.


14

Se si utilizza Spring Boot 2, funziona così com'è, senza alcuna configurazione aggiuntiva.

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

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

non ha funzionato per me, devo usare Spring EL come mostrato sopra.
Bilbo Baggins,

o ancheprivate List<String> myList;
Halayem Anis il

Sì, ha funzionato anche per me: sto usando Spring Boot 2.2.6
Ankit

8

Tutte le risposte sopra sono corrette. Ma puoi raggiungerlo in una sola riga. Prova a seguire la dichiarazione e otterrai tutti i valori separati da virgola in un elenco di stringhe.

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

Inoltre, devi avere la seguente riga definita nella tua configurazione xml.

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

basta sostituire il percorso e il nome del file delle proprietà. E sei a posto. :)

Spero che questo ti aiuti. Saluti.


1
Questo ha funzionato per me, ma ho dovuto pronunciare l'annotazione in questo modo:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven,

6

Se stai utilizzando l'ultima versione del framework Spring (Spring 3.1+ credo), non hai bisogno di suddividere le cose in SpringEL,

Aggiungi semplicemente PropertySourcesPlaceholderConfigurer e DefaultConversionService nella classe Configuration di Spring (quella con annotato con Configuration) ad es.

@Configuration
public class AppConfiguration {

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

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

e nella tua classe

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

e nel file delle proprietà

list=A,B,C,D,E

Senza DefaultConversionService, puoi solo prendere String separate da virgola nell'array String quando inietti il ​​valore nel tuo campo, ma DefaultConversionService fa qualche magia conveniente per te e le aggiungerà in Collection, Array, ecc. (Controlla l'implementazione se vuoi mi piacerebbe saperne di più)

Con questi due, gestisce anche tutti gli spazi bianchi ridondanti, inclusa la nuova riga, quindi non è necessario aggiungere ulteriori logiche per tagliarli.


L'aggiunta di questa configurazione funziona ma succede qualcosa di strano: non posso usare @Value per il tipo di file, devo cambiare il file con la risorsa.
ad3luc,

2

puoi farlo con annotazioni come questa

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

qui my.list.of.strings verrà prelevato dal file delle proprietà, se non è presente, verranno utilizzate le impostazioni predefinite a, b, c

e nel tuo file delle proprietà, puoi avere qualcosa del genere

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


2

Attenzione agli spazi nei valori. Potrei sbagliarmi, ma penso che gli spazi nell'elenco separato da virgole non vengano troncati usando @Value e Spel. La lista

foobar=a, b, c

verrebbe letto come un elenco di stringhe

"a", " b", " c"

Nella maggior parte dei casi probabilmente non vorresti gli spazi!

L'espressione

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

ti darebbe un elenco di stringhe:

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

L'espressione regolare rimuove tutti gli spazi appena prima e subito dopo una virgola. Gli spazi all'interno dei valori non vengono rimossi. Così

foobar = AA, B B, CCC

dovrebbe comportare valori

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

Il metodo split () usa regex internamente come delimitatore, quindi il tuo esempio può essere semplificato: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B il

2

Penso che questo sia più semplice per afferrare l'array e rimuovere gli spazi:

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

2

Nel mio caso di un elenco di numeri interi funziona così:

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

File delle proprietà:

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

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

1

Prendi in considerazione l'utilizzo di Commons Configuration. Ha una funzione integrata per spezzare una voce nel file delle proprietà in array / elenco. La combinazione con SpEL e @Value dovrebbe dare quello che vuoi


Come richiesto, ecco quello che ti serve (non ho davvero provato il codice, potrebbe avere degli errori di battitura, per favore abbi pazienza):

Nella configurazione di Apache Commons è presente PropertiesConfiguration. Supporta la funzione di conversione di stringhe delimitate in array / elenco.

Ad esempio, se si dispone di un file delle proprietà

#Foo.properties
foo=bar1, bar2, bar3

Con il codice seguente:

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

ti darà una serie di stringhe di ["bar1", "bar2", "bar3"]

Per usarlo con Spring, includilo nel tuo xml di contesto dell'app:

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

e hai questo nel tuo fagiolo primaverile:

public class SomeBean {

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

Credo che dovrebbe funzionare: P


potresti essere più specifico sul metodo e sulla classe da utilizzare e l'effettivo sniplet di codice di esempio sarebbe ottimo.
JackDev,

1

Il mio modo preferito (in particolare per le stringhe) è il seguente:

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

e usare

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

In questo modo, puoi includere anche le virgole nel parametro. Funziona anche con i set.


0

se si utilizzano segnaposto di proprietà, diventerebbe l'esempio ser1702544

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

Con segnaposto 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

Sto usando Spring Boot 2.2.6

Il mio file delle proprietà:

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

Il mio codice:

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

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

Funziona bene

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.