319

I want to have a list of values in a .properties file, i.e.:

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

And to load it in my class directly, i.e.:

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

As I understand, an alternative of doing this is to have it in the Spring config file, and load it as a bean reference (correct me if I'm wrong), i.e.

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

But is there any way of doing this using a .properties file?

P.S.

I would like to do this without any custom code if possible.

informatik01
  • 16,038
  • 10
  • 74
  • 104
JackDev
  • 11,003
  • 12
  • 51
  • 68

17 Answers17

591

Using Spring EL:

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

Assuming your properties file is loaded correctly with the following:

my.list.of.strings=ABC,CDE,EFG
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Wilhelm Kleu
  • 10,821
  • 4
  • 36
  • 48
  • Gave me a "Caused by: javax.el.ELException: Failed to parse the expression [#{'${my.list.of.strings}'.split(',')}"] Did it work for you? Maybe I did something incorrectly. – JackDev Oct 03 '12 at 02:26
  • Works perfectly when I try it. What Spring version are you using? – Wilhelm Kleu Oct 03 '12 at 06:08
  • 1
    I checked it again using the same version you are using. Copied the Spring EL exactly as in the post and it works. What is different though is if I make an error in my EL I get a `org.springframework.expression.spel.SpelEvaluationException` exception and not `javax.el.ELException`. Is your objected created by Spring? – Wilhelm Kleu Oct 25 '12 at 08:05
  • 1
    Works perfectly with Spring 3.2. – ehsanullahjan Mar 08 '13 at 02:12
  • 25
    How about empty property `my.list.of.strings=`? I would expect such functionality retruning empty list where here it will be one item (empty string), wouldn't it? – Jan Zyka Aug 29 '14 at 14:09
  • 1
    I created custom static method to do that. First checking whether the input String is null or empty, in that case return empty list. Otherwise do the split. You can call static methods in EL easily. – Jan Zyka Sep 08 '15 at 09:19
  • 5
    Also note that the suggested solution doesn't do trimming so values like `item1, item2, item3` may give you result you don't really expect (hint: spaces). – Jan Zyka Sep 08 '15 at 09:20
  • 12
    If you need to trim empty spaces use regex for split argument.. something like `@Value("#{'${my.list.of.strings}'.split(',\\s*')}") ` – Stackee007 Apr 19 '17 at 21:20
  • 1
    Be careful not to put space before `#{` e.g. `@Value(" #{'${my.list.of.strings}'.split(',')}")` or it won't split property just provide list with one non splitted element. – Danny Dan Apr 03 '18 at 11:11
  • Does it support in XML? – Xiaokun Jul 28 '20 at 10:10
  • How can one read similar property and create a list of String in applicationContext.xml? – Anurag Sep 01 '20 at 08:08
  • This does not work with `@CrossOrigin(origins = {"${allowed.cors.urls}"})` Anyone has solution for annotation level? – Aakash Patel Nov 23 '20 at 11:47
  • Can set be used here in place of list? – TomJava Jul 30 '21 at 11:42
  • when I try to keep a default value for empty list by adding ': '. there's still one element in the list with value of null when i print the list. how to resolve this. @Value("#{'${list.whitelist: }'.split(',')}") List WhiteList – Praneeth Kumar Oct 12 '22 at 12:49
  • my.list.of.strings={'ABC','CDE','EFG'} If you use this syntax, then you don't need to use custom expression to split. – Shailesh Pratapwar May 19 '23 at 13:40
112

Since Spring 3.0, you can add a line like

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

to your applicationContext.xml (or where you configure things). As Dmitry Chornyi points out in a comment, Java based configuration looks like:

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

This activates the new configuration service which supports converting String to Collection types. If you do not activate this configuration service, Spring falls back on its legacy property editors as configuration services, which do not support this kind of conversion.

Converting to collections of other types works, too:

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

will work with a line like

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

No problems with whitespace there, the ConversionServiceFactoryBean takes care of it.

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

In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext). That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework. [...] If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.

Clemens Klein-Robbenhaar
  • 3,457
  • 1
  • 18
  • 27
  • 8
    Java configuration: @Bean public ConversionService conversionService() { return new DefaultConversionService(); } – Dmitry Chornyi Nov 19 '15 at 02:49
  • 2
    Outside avoiding to repeat yourself with `split()` in every expression, it also properly handles an empty list instead of giving you `[null]` – Didier L Oct 10 '17 at 10:22
  • It is not working if the property is overrided (Exists in multiple property sources.) – Daniel Hári Feb 18 '18 at 16:09
60

If you are reading this and you are using Spring Boot, you have 1 more option for this feature

Usually comma separated list are very clumsy for real world use case (And sometime not even feasible, if you want to use commas in your config):

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

With Spring Boot, you can write it like these (Index start at 0):

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

And use it like these:

@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
  • 4,233
  • 2
  • 31
  • 38
  • I have a problem where the other solutions described here do not work due to escaped commas not beeing recognized. Is there a way to do this solution with Spring 4? – sandrozbinden Jul 19 '18 at 12:10
50

If you are using Spring Boot 2, it works as is, without any additional configuration.

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

@Value("${my.list.of.strings}")
private List<String> myList;
Bienvenido David
  • 4,118
  • 1
  • 25
  • 16
47

By specifying the the my.list.of.strings=ABC,CDE,EFG in .properties file and using

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

You can get the arrays of strings. And using CollectionUtils.addAll(myList, myString), you can get the list of strings.

Mukesh Kumar
  • 945
  • 4
  • 11
  • 26
20

Have you considered @Autowireding the constructor or a setter and String.split()ing in the body?

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

I tend to prefer doing my autowiring in one of these ways to enhance the testability of my code.

nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
13

If you are using latest Spring framework version(Spring 3.1+ I believe), you don't need to those string split stuff in SpringEL,

Simply add PropertySourcesPlaceholderConfigurer and DefaultConversionService in your Spring's Configuration class ( the one with annotated with Configuration ) e.g,

@Configuration
public class AppConfiguration {

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

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

and in your class

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

and in the properties file

list=A,B,C,D,E

Without DefaultConversionService, you can only take comma separated String into String array when you inject the value into your field, but DefaultConversionService does a few convenient magic for you and will add those into Collection, Array, etc. ( check the implementation if you'd like to know more about it )

With these two, it even handles all the redundant whitespaces including newline, so you don't need to add additional logics to trim them.

wonhee
  • 1,581
  • 1
  • 14
  • 24
  • The adding of this configuration is working but something strange happens: I cannot use @Value for File type, I must change File with Resource. – ad3luc Jun 06 '19 at 10:20
10

All the above answers are correct. But you can achieve this in just one line. Please try following declaration and you will get all the comma separated values in a String list.

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

And also you need to have the following line defined in your xml configuration.

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

just replace the path and file name of your properties file. And you are good to go. :)

Hope this helps you. Cheers.

Japan Trivedi
  • 4,445
  • 2
  • 23
  • 44
  • 2
    This worked for me, but I had to phrase the annotation like this: `@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")` – Steven Jun 08 '16 at 19:25
9

I am using Spring Boot 2.2.6

My property file:

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

My code:

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

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

It works fine

Ankit
  • 4,426
  • 7
  • 45
  • 62
6

My preferred way (for strings, in particular), is the following one:

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

and use

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

In this way, you can include also commas in your parameter. It works also for Sets.

Sampisa
  • 1,487
  • 2
  • 20
  • 28
  • What/where is the underlying code that does this conversion for Spring? – Toofy Aug 14 '20 at 09:47
  • It's SpELl that reads property admin.user ( the '${admin.user}', see https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions) and then parses what is inside ( the '#{....}' syntax, see https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-value-annotations). – Sampisa Sep 15 '22 at 09:24
4

you can do this with annotations like this

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

here my.list.of.strings will be picked from the properties file, if its not there, then the defaults a,b,c will be used

and in your properties file, you can have something like this

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

verma
  • 753
  • 8
  • 8
3

Beware of spaces in the values. I could be wrong, but I think spaces in the comma-separated list are not truncated using @Value and Spel. The list

foobar=a, b, c

would be read in as a list of strings

"a", " b", " c"

In most cases you would probably not want the spaces!

The expression

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

would give you a list of strings:

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

The regular expression removes all spaces just before and just after a comma. Spaces inside of the values are not removed. So

foobar = AA, B B, CCC

should result in values

"AA", "B B", "CCC".
DAMungra
  • 71
  • 4
  • The split() method uses internally regex as delimiter, so your example can be simplified:
    `@Value("#{'${foobar}'.trim().split( *, *)}")`
    – Karlik_B Dec 06 '18 at 17:44
2

I think this is simpler for grabbing the array and stripping spaces:

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

In my case of a list of integers works this:

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

Property file:

my.list.of.integers={100,200,300,400,999}
Sergey Nemchinov
  • 1,348
  • 15
  • 21
1

Consider using Commons Configuration. It have built in feature to break an entry in properties file to array/list. Combing with SpEL and @Value should give what you want


As requested, here is what you need (Haven't really tried the code, may got some typoes, please bear with me):

In Apache Commons Configuration, there is PropertiesConfiguration. It supports the feature of converting delimited string to array/list.

For example, if you have a properties file

#Foo.properties
foo=bar1, bar2, bar3

With the below code:

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

will give you a string array of ["bar1", "bar2", "bar3"]

To use with Spring, have this in your app context xml:

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

and have this in your spring bean:

public class SomeBean {

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

I believe this should work :P

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • could you be more specific about the method and class to use, and actual sample code sniplet would be great. – JackDev Oct 03 '12 at 04:02
1

The Answer

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

works as expected for the comma separated values. To handle null (when property not specified) added the default value(': 'after the property name) as empty string as below:

@Value("#{'${my.list.of.strings: }'.split(',')}")
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
trumanu1
  • 11
  • 2
0

if using property placeholders then ser1702544 example would become

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

With 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
  • 47
  • 4