Membaca Daftar dari file properti dan memuat dengan pegas penjelasan @ Nilai

244

Saya ingin memiliki daftar nilai dalam file .properties, yaitu:

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

Dan untuk memuatnya di kelas saya secara langsung, yaitu:

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

Seperti yang saya mengerti, alternatif melakukan ini adalah untuk memilikinya di file konfigurasi musim semi, dan memuatnya sebagai referensi kacang (koreksi saya jika saya salah), yaitu

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

Tetapi apakah ada cara untuk melakukan ini? menggunakan file .properties? ps: Saya ingin melakukan ini tanpa kode khusus jika memungkinkan.

JackDev
sumber

Jawaban:

454

Menggunakan Spring EL:

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

Dengan asumsi file properti Anda dimuat dengan benar dengan yang berikut:

my.list.of.strings=ABC,CDE,EFG
Wilhelm Kleu
sumber
1
Saya memeriksanya lagi menggunakan versi yang sama yang Anda gunakan. Menyalin EL Musim Semi persis seperti pada pos dan berfungsi. Yang berbeda adalah jika saya membuat kesalahan pada EL saya, saya mendapatkan org.springframework.expression.spel.SpelEvaluationExceptionpengecualian dan tidak javax.el.ELException. Apakah keberatan Anda dibuat oleh Spring?
Wilhelm Kleu
1
Berfungsi sempurna dengan Spring 3.2.
ehsanullahjan
17
Bagaimana dengan properti kosong my.list.of.strings=? Saya harapkan fungsi seperti itu menyusun kembali daftar kosong di mana ini akan menjadi satu item (string kosong), bukan?
Jan Zyka
5
Perhatikan juga bahwa solusi yang disarankan tidak melakukan pemangkasan sehingga nilai seperti item1, item2, item3dapat memberi Anda hasil yang tidak Anda harapkan (petunjuk: spasi).
Jan Zyka
4
Jika Anda perlu memangkas ruang kosong gunakan regex untuk argumen split .. sesuatu seperti@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007
88

Sejak Spring 3.0, Anda dapat menambahkan garis seperti

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

ke applicationContext.xml(atau di mana Anda mengkonfigurasi hal-hal). Seperti yang ditunjukkan Dmitry Chornyi dalam komentar, konfigurasi berbasis Java terlihat seperti:

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

Ini mengaktifkan layanan konfigurasi baru yang mendukung konversi Stringke Collectiontipe. Jika Anda tidak mengaktifkan layanan konfigurasi ini, Spring mengandalkan editor properti lawasnya sebagai layanan konfigurasi, yang tidak mendukung konversi semacam ini.

Konversi ke koleksi jenis lain juga berfungsi:

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

akan bekerja dengan garis seperti

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

Tidak ada masalah dengan spasi putih di sana, ConversionServiceFactoryBeanmengurusnya.

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

Dalam aplikasi Musim Semi, Anda biasanya mengonfigurasi instance ConversionService per wadah Spring (atau ApplicationContext). ConversionService itu akan diambil oleh Spring dan kemudian digunakan setiap kali konversi tipe perlu dilakukan oleh framework. [...] Jika tidak ada ConversionService terdaftar dengan Spring, sistem berbasis PropertyEditor asli digunakan.

Clemens Klein-Robbenhaar
sumber
6
Konfigurasi Java: @Bean Public ConversionService ConversionService () {mengembalikan DefaultConversionService (); }
Dmitry Chornyi
2
Di luar menghindari untuk mengulangi diri Anda dengan split()dalam setiap ekspresi, itu juga benar menangani daftar kosong bukannya memberi Anda[null]
Didier L
Ini tidak berfungsi jika properti
diganti
47

Jika Anda membaca ini dan Anda menggunakan Spring Boot , Anda memiliki 1 opsi lagi untuk fitur ini

Biasanya daftar yang dipisahkan koma sangat canggung untuk kasus penggunaan dunia nyata (Dan kadang-kadang bahkan tidak layak, jika Anda ingin menggunakan koma di konfigurasi Anda):

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

Dengan Spring Boot , Anda dapat menulis seperti ini (Indeks mulai dari 0):

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

Dan gunakan seperti ini:

@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
sumber
Saya memiliki masalah ketika solusi lain yang dijelaskan di sini tidak berfungsi karena koma yang lolos tidak dapat dikenali. Apakah ada cara untuk melakukan solusi ini dengan Spring 4?
sandrozbinden
34

Dengan menentukan my.list.of.strings=ABC,CDE,EFGfile in .properties dan menggunakan

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

Anda bisa mendapatkan array string. Dan menggunakan CollectionUtils.addAll(myList, myString), Anda bisa mendapatkan daftar string.

Mukesh Kumar
sumber
Saya hanya mendapatkan String pertama "ABC"
Osama Abdulsattar
19

Sudahkah Anda mempertimbangkan @Autowiredkonstruktor atau setter dan String.split()ing dalam tubuh?

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

Saya cenderung lebih suka melakukan autowiring saya dalam salah satu cara ini untuk meningkatkan testabilitas kode saya.

nicholas.hauschild
sumber
14

Jika Anda menggunakan Spring Boot 2, itu berfungsi apa adanya, tanpa konfigurasi tambahan.

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

@Value("${my.list.of.strings}")
private List<String> myList;
Bienvenido David
sumber
tidak bekerja untuk saya, saya harus menggunakan Spring EL seperti yang ditunjukkan di atas.
Bilbo Baggins
atau bahkanprivate List<String> myList;
Halayem Anis
Ya itu bekerja untuk saya juga: Saya menggunakan Spring Boot 2.2.6
Ankit
8

Semua jawaban di atas benar. Tetapi Anda dapat mencapai ini hanya dalam satu baris. Silakan coba deklarasi berikut ini dan Anda akan mendapatkan semua nilai yang dipisahkan koma dalam daftar String.

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

Dan juga Anda harus memiliki baris berikut yang ditentukan dalam konfigurasi xml Anda.

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

cukup ganti jalur dan nama file dari file properti Anda. Dan Anda baik untuk pergi. :)

Semoga ini bisa membantu Anda. Bersulang.

Trivedi Jepang
sumber
1
Ini berhasil untuk saya, tetapi saya harus mengucapkan anotasi seperti ini:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven
6

Jika Anda menggunakan versi Spring framework terbaru (Spring 3.1+ saya percaya), Anda tidak perlu hal-hal string split di SpringEL,

Cukup tambahkan PropertySourcesPlaceholderConfigurer dan DefaultConversionService di kelas Konfigurasi Spring Anda (yang dengan penjelasan dengan Konfigurasi) misalnya,

@Configuration
public class AppConfiguration {

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

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

dan di kelasmu

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

dan di file properti

list=A,B,C,D,E

Tanpa DefaultConversionService, Anda hanya dapat mengambil String yang dipisahkan koma ke dalam array String ketika Anda menyuntikkan nilai ke bidang Anda, tetapi DefaultConversionService melakukan beberapa keajaiban yang mudah bagi Anda dan akan menambahkannya ke dalam Collection, Array, dll. (Periksa implementasinya jika Anda ingin ingin tahu lebih banyak tentang itu)

Dengan keduanya, ini bahkan menangani semua spasi putih berlebihan termasuk baris baru, jadi Anda tidak perlu menambahkan logika tambahan untuk memotongnya.

wonhee
sumber
Penambahan konfigurasi ini berfungsi tetapi sesuatu yang aneh terjadi: Saya tidak dapat menggunakan @ Nilai untuk tipe File, saya harus mengubah File dengan Sumber Daya.
ad3luc
2

Anda dapat melakukan ini dengan anotasi seperti ini

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

di sini my.list.of.strings akan diambil dari file properti, jika tidak ada, maka default a, b, c akan digunakan

dan di file properti Anda, Anda dapat memiliki sesuatu seperti ini

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

verma
sumber
2

Waspadalah terhadap spasi dalam nilai. Saya bisa saja salah, tapi saya pikir spasi di daftar yang dipisahkan koma tidak terpotong menggunakan @Value dan Spel. Daftar

foobar=a, b, c

akan dibaca sebagai daftar string

"a", " b", " c"

Dalam kebanyakan kasus, Anda mungkin tidak ingin spasi!

Ekspresi

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

akan memberi Anda daftar string:

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

Ekspresi reguler menghapus semua spasi sebelum dan sesudah koma. Spasi di dalam nilai tidak dihapus. Begitu

foobar = AA, B B, CCC

harus menghasilkan nilai

"AA", "B B", "CCC".
DAMungra
sumber
Metode split () menggunakan regex internal sebagai pembatas, sehingga contoh Anda dapat disederhanakan: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B
2

Saya pikir ini lebih mudah untuk meraih array dan stripping space:

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

Dalam kasus saya daftar bilangan bulat berfungsi sebagai berikut:

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

File properti:

my.list.of.integers={100,200,300,400,999}
Sergey Nemchinov
sumber
atau @Value ("# {$ {my.set.of.integers}}") set pribadi <Integer> setOfIntegers;
Alexey Simonov
1

Pertimbangkan untuk menggunakan Konfigurasi Commons. Ini memiliki fitur bawaan untuk memecah entri dalam file properti ke array / daftar. Menyisir dengan SpEL dan @Value akan memberikan apa yang Anda inginkan


Seperti yang diminta, inilah yang Anda butuhkan (Belum benar-benar mencoba kode, mungkin mendapat beberapa kesalahan ketik, mohon ikut saya):

Di Konfigurasi Apache Commons, ada PropertiesConfiguration. Ini mendukung fitur konversi string terbatas ke array / daftar.

Misalnya, jika Anda memiliki file properti

#Foo.properties
foo=bar1, bar2, bar3

Dengan kode di bawah ini:

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

akan memberi Anda array string ["bar1", "bar2", "bar3"]

Untuk digunakan dengan Spring, dapatkan ini dalam konteks aplikasi Anda xml:

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

dan memilikinya di kacang musim semi Anda:

public class SomeBean {

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

Saya percaya ini harus berhasil: P

Adrian Shum
sumber
dapatkah Anda lebih spesifik tentang metode dan kelas yang akan digunakan, dan sniplet kode sampel aktual akan lebih bagus.
JackDev
1

Cara pilihan saya (untuk string, khususnya), adalah yang berikut:

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

dan gunakan

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

Dengan cara ini, Anda dapat memasukkan juga koma di parameter Anda. Ini juga berfungsi untuk Set.

Sampisa
sumber
0

jika menggunakan placeholder properti maka contoh ser1702544 akan menjadi

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

Dengan placeholder 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
sumber
0

Saya menggunakan Spring Boot 2.2.6

File properti saya:

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

Kode saya:

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

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

Ini bekerja dengan baik

Ankit
sumber