Odczytywanie listy z pliku właściwości i ładowanie z adnotacją sprężynową @Wartość

244

Chcę mieć listę wartości w pliku .properties, tj .:

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

Aby załadować go bezpośrednio do mojej klasy, tj .:

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

Jak rozumiem, alternatywą takiego postępowania jest umieszczenie go w wiosennym pliku konfiguracyjnym i załadowanie go jako odwołania do komponentu bean (popraw mnie, jeśli się mylę), tj.

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

Ale czy jest na to sposób? używasz pliku .properties? ps: Chciałbym to zrobić bez kodu niestandardowego, jeśli to możliwe.

JackDev
źródło

Odpowiedzi:

454

Za pomocą Spring EL:

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

Zakładając, że plik właściwości jest poprawnie załadowany z następującymi elementami:

my.list.of.strings=ABC,CDE,EFG
Wilhelm Kleu
źródło
1
Sprawdziłem to ponownie, używając tej samej wersji, której używasz. Skopiowałem Spring EL dokładnie tak, jak w poście i działa. Różnica polega jednak na tym, że jeśli popełniam błąd w moim EL, dostaję org.springframework.expression.spel.SpelEvaluationExceptionwyjątek i nie javax.el.ELException. Czy Twój obiekt został stworzony przez Spring?
Wilhelm Kleu,
1
Działa idealnie z Spring 3.2.
ehsanullahjan
17
Co powiesz na pustą nieruchomość my.list.of.strings=? Oczekiwałbym, że taka funkcjonalność ponownie przestraszy pustą listę, gdzie tutaj będzie jeden element (pusty ciąg), prawda?
Jan Zyka
5
Zauważ też, że sugerowane rozwiązanie nie obejmuje przycinania, więc wartości takie item1, item2, item3mogą dać wynik, którego tak naprawdę nie oczekujesz (wskazówka: spacje).
Jan Zyka
4
Jeśli chcesz przyciąć puste miejsca, użyj wyrażenia regularnego do podzielenia argumentu .. coś w stylu@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007
88

Od wiosny 3.0 możesz dodać linię podobną do

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

do twojego applicationContext.xml(lub gdzie konfigurujesz rzeczy). Jak zauważa Dmitry Chornyi w komentarzu, konfiguracja oparta na Javie wygląda następująco:

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

Aktywuje to nową usługę konfiguracji, która obsługuje konwersję Stringna Collectiontypy. Jeśli nie aktywujesz tej usługi konfiguracji, Spring powróci do swoich starszych edytorów właściwości jako usług konfiguracji, które nie obsługują tego rodzaju konwersji.

Działa również konwersja do kolekcji innych typów:

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

będzie działać z linią podobną do

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

Nie ma problemów z białymi znakami, ConversionServiceFactoryBeandba o to.

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

W aplikacji Spring zwykle konfiguruje się instancję ConversionService dla kontenera Spring (lub ApplicationContext). Ta usługa konwersji zostanie odebrana przez Springa, a następnie wykorzystana za każdym razem, gdy struktura będzie musiała przeprowadzić konwersję typu. [...] Jeśli w Spring nie zarejestrowano usługi ConversionService, używany jest oryginalny system oparty na PropertyEditor.

Clemens Klein-Robbenhaar
źródło
6
Konfiguracja Java: @Bean public ConversionServiceversionService () {return new DefaultConversionService (); }
Dmitry Chornyi
2
Poza unikaniem powtarzania się split()w każdym wyrażeniu, poprawnie obsługuje pustą listę zamiast dać ci[null]
Didier L
Nie działa, jeśli właściwość jest przesłonięta (Występuje w wielu źródłach właściwości.)
Daniel Hári
47

Jeśli czytasz to i używasz Spring Boot , masz 1 dodatkową opcję dla tej funkcji

Zazwyczaj lista oddzielona przecinkami jest bardzo nieporęczna w przypadku użycia w świecie rzeczywistym (a czasami nawet niewykonalne, jeśli chcesz używać przecinków w konfiguracji):

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

W Spring Boot możesz to napisać w następujący sposób (Początek indeksu od 0):

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

I użyj tego w następujący sposób:

@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
Ng Sek Long
źródło
Mam problem polegający na tym, że inne opisane tutaj rozwiązania nie działają z powodu przecinków, które nie zostały rozpoznane. Czy istnieje sposób na rozwiązanie tego problemu za pomocą Spring 4?
sandrozbinden
34

Określając my.list.of.strings=ABC,CDE,EFGplik .properties i używając

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

Możesz uzyskać tablice ciągów. I za pomocą CollectionUtils.addAll(myList, myString)możesz uzyskać listę ciągów.

Mukesh Kumar
źródło
Dostaję tylko pierwszy ciąg „ABC”
Osama Abdulsattar
19

Czy rozważałeś @Autowiredwprowadzenie konstruktora lub setera i String.split()wtopienie się w ciało?

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

Wolę robić autowire na jeden z tych sposobów, aby zwiększyć testowalność mojego kodu.

nicholas.hauschild
źródło
14

Jeśli używasz Spring Boot 2, działa tak, jak jest, bez dodatkowej konfiguracji.

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

@Value("${my.list.of.strings}")
private List<String> myList;
Bienvenido David
źródło
nie działało dla mnie, muszę użyć Spring EL, jak pokazano powyżej.
Bilbo Baggins
a nawetprivate List<String> myList;
Halayem Anis,
Tak, to zadziałało również dla mnie: używam Spring Boot 2.2.6
Ankit
8

Wszystkie powyższe odpowiedzi są poprawne. Ale możesz to osiągnąć w jednej linii. Spróbuj wykonać następującą deklarację, a otrzymasz wszystkie wartości oddzielone przecinkami na liście ciągów.

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

Musisz także zdefiniować następujący wiersz w konfiguracji xml.

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

wystarczy zastąpić ścieżkę i nazwę pliku właściwości. I jesteś gotowy iść. :)

Mam nadzieję, że to ci pomoże. Twoje zdrowie.

Japan Trivedi
źródło
1
To zadziałało dla mnie, ale musiałem sformułować adnotację w następujący sposób:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven
6

Jeśli używasz najnowszej wersji Spring Framework (uważam, że Spring 3.1+), nie potrzebujesz tych elementów rozdzielających ciąg w SpringEL,

Po prostu dodaj PropertySourcesPlaceholderConfigurer i DefaultConversionService do klasy konfiguracji Springa (tej z adnotacją do konfiguracji), np.

@Configuration
public class AppConfiguration {

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

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

i w twojej klasie

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

oraz w pliku właściwości

list=A,B,C,D,E

Bez DefaultConversionService możesz wstawić ciąg rozdzielany przecinkami do tablicy String, gdy wstrzykniesz wartość do swojego pola, ale DefaultConversionService zrobi dla ciebie kilka dogodnych magii i doda je do Collection, Array itp. (Sprawdź implementację, jeśli chcesz lubię wiedzieć o tym więcej)

Dzięki tym dwóm obsługuje nawet wszystkie nadmiarowe białe znaki, w tym znak nowej linii, więc nie trzeba dodawać dodatkowych elementów logicznych, aby je przyciąć.

wonhee
źródło
Dodanie tej konfiguracji działa, ale dzieje się coś dziwnego: nie mogę użyć @Value dla typu pliku, muszę zmienić plik z zasobem.
ad3luc
2

możesz to zrobić za pomocą takich adnotacji

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

tutaj moja.list.of.strings zostanie wybrana z pliku właściwości, jeśli go nie ma, zostaną użyte wartości domyślne a, b, c

a w pliku właściwości możesz mieć coś takiego

moja.lista.strun = d, e, f

Verma
źródło
2

Uważaj na spacje w wartościach. Mogę się mylić, ale myślę, że spacje na liście oddzielonej przecinkami nie są obcinane za pomocą @Value i Spel. Lista

foobar=a, b, c

zostanie odczytany jako lista ciągów

"a", " b", " c"

W większości przypadków prawdopodobnie nie chcesz spacji!

Ekspresja

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

dałby ci listę ciągów:

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

Wyrażenie regularne usuwa wszystkie spacje tuż przed przecinkiem i tuż po nim. Spacje wewnątrz wartości nie są usuwane. Więc

foobar = AA, B B, CCC

powinno dawać wartości

"AA", "B B", "CCC".
DAMungra
źródło
Metoda split () używa wewnętrznie wyrażenia regularnego jako separatora, więc twój przykład można uprościć: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B
2

Myślę, że łatwiej jest chwytać tablicę i usuwać spacje:

@Value("#{'${my.array}'.replace(' ', '').split(',')}")
private List<String> array;
Mike Samaras
źródło
2

W moim przypadku działa lista liczb całkowitych:

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

Plik właściwości:

my.list.of.integers={100,200,300,400,999}
Siergiej Niemczinow
źródło
lub @Value („# {$ {my.set.of.integers}}”) prywatny Set <Integer> setOfIntegers;
Alexey Simonov
1

Rozważ użycie konfiguracji Commons. Ma wbudowaną funkcję przerywania wpisu w pliku właściwości do tablicy / listy. Czesanie ze SpEL i @Value powinno dać ci to, czego chcesz


Zgodnie z prośbą, oto czego potrzebujesz (naprawdę nie wypróbowałem kodu, może mam kilka błędów, proszę o wyrozumiałość):

W konfiguracji Apache Commons istnieje PropertiesConfiguration. Obsługuje funkcję konwertowania łańcucha rozdzielanego na tablicę / listę.

Na przykład, jeśli masz plik właściwości

#Foo.properties
foo=bar1, bar2, bar3

Z poniższym kodem:

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

da ci tablicę ciągów ["bar1", "bar2", "bar3"]

Aby używać z Springem, należy mieć to w kontekście swojej aplikacji xml:

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

i miej to w swojej fasoli wiosennej:

public class SomeBean {

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

Uważam, że to powinno działać: P

Adrian Shum
źródło
możesz bardziej szczegółowo określić metodę i klasę, której użyjesz, a sam fragment kodu przykładowego byłby świetny.
JackDev,
1

Mój preferowany sposób (w szczególności dla ciągów znaków) jest następujący:

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

I użyć

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

W ten sposób możesz dodać przecinki do swojego parametru. Działa również w przypadku zestawów.

Sampisa
źródło
0

jeśli użyjesz symboli zastępczych właściwości, wówczas będzie to przykład ser1702544

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

Z symbolem zastępczym 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> 
cgull
źródło
0

Używam Spring Boot 2.2.6

Mój plik właściwości:

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

Mój kod:

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

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

To działa dobrze

Ankit
źródło