การอ่านรายการจากไฟล์คุณสมบัติและโหลดด้วยคำอธิบายประกอบแบบสปริง @Value


244

ฉันต้องการมีรายการค่าในไฟล์. properties เช่น:

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

และโหลดในชั้นเรียนของฉันโดยตรงเช่น:

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

ตามที่ฉันเข้าใจทางเลือกอื่นของการทำเช่นนี้คือมีไว้ในไฟล์สปริงสปริงและโหลดเป็นการอ้างอิงถั่ว (แก้ไขฉันถ้าฉันผิด) เช่น

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

แต่มีวิธีการทำเช่นนี้หรือไม่? ใช้ไฟล์. properties หรือไม่ ps: ฉันต้องการทำสิ่งนี้โดยไม่ใช้รหัสที่กำหนดเองถ้าเป็นไปได้

คำตอบ:


454

ใช้ Spring EL:

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

สมมติว่าไฟล์คุณสมบัติของคุณโหลดอย่างถูกต้องดังต่อไปนี้:

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

1
ฉันตรวจสอบอีกครั้งโดยใช้เวอร์ชันเดียวกับที่คุณใช้ คัดลอก Spring EL ตรงตามที่โพสต์และใช้งานได้ คืออะไร แต่ที่แตกต่างกันคือถ้าผมทำผิดพลาดในของฉัน EL ฉันได้รับข้อยกเว้นและไม่ได้org.springframework.expression.spel.SpelEvaluationException javax.el.ELExceptionคุณถูกคัดค้านโดยสปริงหรือไม่?
Wilhelm Kleu

1
ทำงานได้อย่างสมบูรณ์แบบด้วย Spring 3.2
ehsanullahjan

17
แล้วทรัพย์สินเปล่าmy.list.of.strings=ล่ะ? ฉันคาดหวังว่าฟังก์ชั่นดังกล่าวจะเรียกรายการว่างที่นี่ซึ่งจะเป็นหนึ่งรายการ (สตริงว่าง) ใช่ไหม
Jan Zyka

5
นอกจากนี้โปรดทราบว่าโซลูชันที่แนะนำไม่ได้ทำการตัดทอนดังนั้นค่าเช่นitem1, item2, item3นี้อาจให้ผลลัพธ์ที่คุณไม่คาดหวังจริงๆ (คำใบ้: ช่องว่าง)
Jan Zyka

4
หากคุณต้องการตัดพื้นที่ว่างให้ใช้ regex เพื่อหาข้อโต้แย้งแยก .. สิ่งที่ต้องการ@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007

88

ตั้งแต่ Spring 3.0 คุณสามารถเพิ่มบรรทัดได้

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

ไปที่applicationContext.xml(หรือที่คุณกำหนดค่าสิ่งต่าง ๆ ) ตามที่ Dmitry Chornyi ชี้ให้เห็นในความคิดเห็นการกำหนดค่าบน Java ดูเหมือนว่า:

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

สิ่งนี้จะเปิดใช้งานบริการการกำหนดค่าใหม่ซึ่งรองรับการแปลงStringเป็นCollectionประเภท หากคุณไม่เปิดใช้งานบริการกำหนดค่านี้สปริงจะกลับมาใช้งานกับตัวแก้ไขคุณสมบัติดั้งเดิมเป็นบริการกำหนดค่าซึ่งไม่รองรับการแปลงประเภทนี้

การแปลงเป็นคอลเล็กชันประเภทอื่น ๆ ก็ใช้ได้เช่นกัน:

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

จะทำงานกับสายเช่น

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

ไม่มีปัญหากับช่องว่างที่นั่นConversionServiceFactoryBeanดูแลมัน

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

ในแอปพลิเคชัน Spring โดยทั่วไปคุณจะกำหนดค่าอินสแตนซ์ ConversionService สำหรับแต่ละคอนเทนเนอร์ของ Spring (หรือ ApplicationContext) ConversionService นั้นจะรับโดย Spring และจะใช้ทุกครั้งที่การแปลงประเภทจะต้องดำเนินการโดยเฟรมเวิร์ก [... ] หากไม่มีการลงทะเบียน ConversionService กับ Spring ระบบที่ใช้ PropertyEditor ดั้งเดิมจะถูกใช้


6
การกำหนดค่า Java: @Bean public ConversionService conversionService () {ส่งคืน DefaultConversionService ใหม่ (); }
Dmitry Chornyi

2
นอกการหลีกเลี่ยงการทำซ้ำตัวเองsplit()ในทุก ๆ นิพจน์มันยังจัดการรายการว่างแทนการให้คุณอย่างถูกต้อง[null]
Didier L

มันไม่ทำงานหากสถานที่นั้นถูกแทนที่ (มีอยู่ในหลายแหล่งที่มาของทรัพย์สิน)
Daniel Hári

47

หากคุณกำลังอ่านข้อความนี้และคุณกำลังใช้Spring Bootคุณจะมีตัวเลือกอีก 1 ตัวสำหรับคุณสมบัตินี้

รายการที่คั่นด้วยเครื่องหมายจุลภาคโดยทั่วไปจะค่อนข้างซุ่มซ่ามสำหรับกรณีการใช้งานจริง (และบางครั้งอาจไม่เป็นไปได้ถ้าคุณต้องการใช้เครื่องหมายจุลภาคในการกำหนดค่าของคุณ):

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

ด้วยSpring Bootคุณสามารถเขียนได้เช่นนี้ (ดัชนีเริ่มต้นที่ 0):

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

และใช้มันเช่นนี้

@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

ฉันมีปัญหาที่วิธีแก้ไขปัญหาอื่น ๆ ที่อธิบายไว้ที่นี่ไม่ทำงานเนื่องจากคอมม่าหนีไม่ได้รับการยอมรับ มีวิธีการแก้ปัญหานี้กับ Spring 4 หรือไม่?
sandrozbinden

34

โดยการระบุmy.list.of.strings=ABC,CDE,EFGไฟล์ in .properties และการใช้

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

คุณสามารถรับอาร์เรย์ของสตริงได้ และการใช้CollectionUtils.addAll(myList, myString)งานคุณสามารถรับรายการสตริงได้


ฉันได้รับเฉพาะสตริงแรก "ABC"
Osama Abdulsattar

19

คุณเคยคิด@Autowiredที่จะสร้างคอนสตรัคเตอร์หรือเซ็ตเทอร์แล้วString.split()เข้าร่างกายใช่ไหม

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

ฉันมักจะชอบทำการลงทะเบียนอัตโนมัติด้วยวิธีใดวิธีหนึ่งต่อไปนี้เพื่อเพิ่มความสามารถในการทดสอบโค้ดของฉัน


14

หากคุณใช้ Spring Boot 2 จะทำงานตามที่เป็นอยู่โดยไม่มีการกำหนดค่าเพิ่มเติมใด ๆ

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

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

ไม่ได้ผลสำหรับฉันฉันต้องใช้ Spring EL ดังที่แสดงด้านบน
Bilbo Baggins

หรือแม้กระทั่งprivate List<String> myList;
Halayem Anis

ใช่มันใช้งานได้สำหรับฉันเช่นกัน: ฉันใช้ Spring Boot 2.2.6
Ankit

8

คำตอบทั้งหมดข้างต้นถูกต้อง แต่คุณสามารถทำได้ในหนึ่งบรรทัด โปรดลองทำตามการประกาศและคุณจะได้รับค่าที่คั่นด้วยเครื่องหมายจุลภาคทั้งหมดในรายการสตริง

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

และคุณต้องมีบรรทัดต่อไปนี้ที่กำหนดไว้ในการกำหนดค่า xml ของคุณ

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

เพียงแทนที่พา ธ และชื่อไฟล์ของไฟล์คุณสมบัติของคุณ และคุณจะไปดี :)

หวังว่านี่จะช่วยคุณได้ ไชโย


1
สิ่งนี้ใช้งานได้สำหรับฉัน แต่ฉันต้องใช้วลีประกอบคำอธิบายเช่นนี้:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven

6

หากคุณกำลังใช้เวอร์ชันเฟรมเวิร์กสปริงล่าสุด (สปริง 3.1+ ฉันเชื่อว่า) คุณไม่จำเป็นต้องแยกสตริงเหล่านั้นใน SpringEL

เพียงเพิ่ม PropertySourcesPlaceholderConfigurer และ DefaultConversionService ในคลาสการกำหนดค่าของ Spring ของคุณ (อันที่มีหมายเหตุประกอบกับการกำหนดค่า) เช่น

@Configuration
public class AppConfiguration {

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

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

และในชั้นเรียนของคุณ

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

และในไฟล์คุณสมบัติ

list=A,B,C,D,E

หากไม่มี DefaultConversionService คุณสามารถนำสตริงที่คั่นด้วยเครื่องหมายจุลภาคไปยังสตริงอาร์เรย์ได้เมื่อคุณฉีดค่าลงในฟิลด์ของคุณ แต่ DefaultConversionService จะทำเวทมนตร์ที่สะดวกสำหรับคุณและจะเพิ่มสิ่งเหล่านั้นลงใน Collection, Array และอื่น ๆ (ตรวจสอบการนำไปใช้ ต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับมัน)

ด้วยสองสิ่งนี้มันยังจัดการกับช่องว่างที่ซ้ำซ้อนทั้งหมดรวมถึงการขึ้นบรรทัดใหม่ดังนั้นคุณจึงไม่จำเป็นต้องเพิ่ม logics เพิ่มเติมเพื่อตัดแต่ง


การเพิ่มการกำหนดค่านี้ใช้งานได้ แต่มีสิ่งผิดปกติเกิดขึ้น: ฉันไม่สามารถใช้ @Value สำหรับประเภทไฟล์ได้ฉันต้องเปลี่ยนไฟล์ด้วยทรัพยากร
ad3luc

2

คุณสามารถทำได้ด้วยคำอธิบายประกอบเช่นนี้

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

ที่นี่ my.list.of.strings จะถูกเลือกจากไฟล์คุณสมบัติหากไม่มีอยู่ก็จะใช้ค่าเริ่มต้น a, b, c

และในไฟล์คุณสมบัติของคุณคุณสามารถมีสิ่งนี้

my.list.of.strings = D, E, F


2

ระวังช่องว่างในค่า ฉันอาจจะผิด แต่ฉันคิดว่าช่องว่างในรายการที่คั่นด้วยเครื่องหมายจุลภาคจะไม่ถูกตัดโดยใช้ @Value และ Spel รายการ

foobar=a, b, c

จะอ่านเป็นรายการสตริง

"a", " b", " c"

ในกรณีส่วนใหญ่คุณอาจไม่ต้องการช่องว่าง!

การแสดงออก

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

จะให้รายการสตริง:

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

การแสดงออกปกติลบช่องว่างทั้งหมดก่อนและหลังเครื่องหมายจุลภาค ช่องว่างด้านในของค่าจะไม่ถูกลบออก ดังนั้น

foobar = AA, B B, CCC

ควรส่งผลให้ค่า

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

วิธี split () ใช้ regex ภายในเป็นตัวคั่นดังนั้นตัวอย่างของคุณสามารถทำให้ง่ายขึ้น: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B


2

ในกรณีของฉันรายการจำนวนเต็มงานนี้

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

ไฟล์คุณสมบัติ:

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

หรือ @Value ("# {$ {my.set.of.integers}}") ชุดส่วนตัว <Integer> setOfIntegers;
Alexey Simonov

1

พิจารณาใช้การกำหนดค่าทั่วไป มันมีคุณสมบัติในตัวที่จะทำลายรายการในไฟล์คุณสมบัติเพื่ออาร์เรย์ / รายการ การผสมผสานกับ SpEL และ @Value ควรให้สิ่งที่คุณต้องการ


ตามที่ร้องขอนี่คือสิ่งที่คุณต้องการ (ยังไม่ได้ลองรหัสจริง ๆ อาจพิมพ์ผิดโปรดอดทนกับฉัน):

ใน Apache Commons Configuration มีคุณสมบัติการกำหนดค่า สนับสนุนคุณสมบัติของการแปลงสตริงที่คั่นด้วยอาร์เรย์ / รายการ

ตัวอย่างเช่นถ้าคุณมีไฟล์คุณสมบัติ

#Foo.properties
foo=bar1, bar2, bar3

ด้วยรหัสด้านล่าง:

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

จะให้อาร์เรย์สตริงของ ["bar1", "bar2", "bar3"]

หากต้องการใช้กับ Spring ให้ใช้สิ่งนี้ในบริบทแอป xml ของคุณ:

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

และมีสิ่งนี้ใน Spring spring ของคุณ:

public class SomeBean {

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

ฉันเชื่อว่าสิ่งนี้ควรจะได้ผล: หน้า


คุณจะเจาะจงมากขึ้นเกี่ยวกับวิธีการและคลาสที่จะใช้และตัวอย่างโค้ดจริงจะดี
JackDev

1

วิธีที่ฉันชอบ (สำหรับสตริงโดยเฉพาะ) เป็นวิธีต่อไปนี้:

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

และการใช้งาน

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

ด้วยวิธีนี้คุณสามารถรวมเครื่องหมายจุลภาคในพารามิเตอร์ของคุณได้เช่นกัน มันใช้งานได้กับชุด


0

หากใช้ตัวยึดตำแหน่งคุณสมบัติตัวอย่างของ ser1702544 จะกลายเป็น

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

ด้วยตัวยึดตำแหน่ง 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

ฉันใช้ Spring Boot 2.2.6

ไฟล์คุณสมบัติของฉัน:

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

รหัสของฉัน:

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

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

มันใช้งานได้ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.