Lectura de una lista del archivo de propiedades y carga con anotación de resorte @Value

244

Quiero tener una lista de valores en un archivo .properties, es decir:

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

Y para cargarlo en mi clase directamente, es decir:

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

Según tengo entendido, una alternativa para hacer esto es tenerlo en el archivo de configuración de Spring y cargarlo como referencia de bean (corrígeme si me equivoco), es decir

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

¿Pero hay alguna forma de hacer esto? utilizando un archivo .properties? ps: me gustaría hacer esto sin ningún código personalizado si es posible.

JackDev
fuente

Respuestas:

454

Usando Spring EL:

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

Suponiendo que su archivo de propiedades se carga correctamente con lo siguiente:

my.list.of.strings=ABC,CDE,EFG
Wilhelm Kleu
fuente
1
Lo revisé nuevamente usando la misma versión que está usando. Copié el Spring EL exactamente como en la publicación y funciona. Sin embargo, lo que es diferente es que si cometo un error en mi EL, obtengo una org.springframework.expression.spel.SpelEvaluationExceptionexcepción y no javax.el.ELException. ¿Tu objeción es creada por Spring?
Wilhelm Kleu
1
Funciona perfectamente con Spring 3.2.
ehsanullahjan
17
¿Qué hay de la propiedad vacía my.list.of.strings=? Esperaría tal funcionalidad reajustando la lista vacía donde aquí será un elemento (cadena vacía), ¿no?
Jan Zyka
55
También tenga en cuenta que la solución sugerida no hace recortes, por lo que valores como item1, item2, item3pueden dar un resultado que realmente no espera (pista: espacios).
Jan Zyka
44
Si necesita recortar espacios vacíos, use expresiones regulares para el argumento dividido ... algo así como@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007
88

Desde Spring 3.0, puede agregar una línea como

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

a su applicationContext.xml(o donde configura las cosas). Como Dmitry Chornyi señala en un comentario, la configuración basada en Java se ve así:

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

Esto activa el nuevo servicio de configuración que admite la conversión Stringa Collectiontipos. Si no activa este servicio de configuración, Spring recurre a sus editores de propiedades heredados como servicios de configuración, que no admiten este tipo de conversión.

La conversión a colecciones de otros tipos también funciona:

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

funcionará con una línea como

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

No hay problemas con el espacio en blanco allí, ConversionServiceFactoryBeanse encarga de eso.

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

En una aplicación Spring, normalmente configura una instancia de ConversionService por contenedor Spring (o ApplicationContext). Spring utilizará ese ConversionService y luego lo usará cada vez que el marco deba realizar una conversión de tipo. [...] Si no se registra ConversionService con Spring, se utiliza el sistema original basado en PropertyEditor.

Clemens Klein-Robbenhaar
fuente
66
Configuración de Java: @Bean public ConversionService conversionService () {return new DefaultConversionService (); }
Dmitry Chornyi
2
Fuera de evitar repetirse split()en cada expresión, también maneja adecuadamente una lista vacía en lugar de darte[null]
Didier L
No funciona si se anula la propiedad (existe en varias fuentes de propiedad).
Daniel Hári
47

Si está leyendo esto y está utilizando Spring Boot , tiene 1 opción más para esta función

Por lo general, la lista separada por comas es muy torpe para el caso de uso del mundo real (y a veces ni siquiera es factible, si desea usar comas en su configuración):

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

Con Spring Boot , puede escribirlo así (el índice comienza en 0):

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

Y úsalo así:

@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
fuente
Tengo un problema en el que las otras soluciones descritas aquí no funcionan debido a que las comas escapadas no se reconocen. ¿Hay alguna manera de hacer esta solución con Spring 4?
sandrozbinden
34

Al especificar el my.list.of.strings=ABC,CDE,EFGarchivo in .properties y usar

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

Puede obtener las matrices de cadenas. Y usando CollectionUtils.addAll(myList, myString), puede obtener la lista de cadenas.

Mukesh Kumar
fuente
Solo obtengo la primera cadena "ABC"
Osama Abdulsattar
19

¿Has considerado @Autowiredincorporar al constructor o un setter e String.split()incorporar al cuerpo?

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

Tiendo a preferir hacer mi cableado automático de una de estas formas para mejorar la capacidad de prueba de mi código.

nicholas.hauschild
fuente
14

Si está utilizando Spring Boot 2, funciona como está, sin ninguna configuración adicional.

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

@Value("${my.list.of.strings}")
private List<String> myList;
Bienvenido David
fuente
no funcionó para mí, tengo que usar Spring EL como se muestra arriba.
Bilbo Baggins
o inclusoprivate List<String> myList;
Halayem Anis
Sí, también funcionó para mí: estoy usando Spring Boot 2.2.6
Ankit
8

Todas las respuestas anteriores son correctas. Pero puede lograr esto en una sola línea. Intente con la siguiente declaración y obtendrá todos los valores separados por comas en una lista de cadenas.

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

Y también debe tener la siguiente línea definida en su configuración xml.

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

simplemente reemplace la ruta y el nombre del archivo de propiedades. Y eres bueno para ir. :)

Espero que esto te ayude. Salud.

Japan Trivedi
fuente
1
Esto funcionó para mí, pero tuve que expresar la anotación de esta manera:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven
6

Si está utilizando la última versión de Spring Framework (Spring 3.1+, creo), no necesita esas cosas de división de cadenas en SpringEL,

Simplemente agregue PropertySourcesPlaceholderConfigurer y DefaultConversionService en la clase de configuración de Spring (la que está anotada con Configuration), por ejemplo,

@Configuration
public class AppConfiguration {

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

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

y en tu clase

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

y en el archivo de propiedades

list=A,B,C,D,E

Sin DefaultConversionService, solo puede tomar una cadena separada por comas en una matriz de cadenas cuando inyecta el valor en su campo, pero DefaultConversionService hace algunas magias convenientes para usted y las agregará a Collection, Array, etc. (verifique la implementación si desea me gustaría saber más al respecto)

Con estos dos, incluso maneja todos los espacios en blanco redundantes, incluida la nueva línea, por lo que no necesita agregar lógicas adicionales para recortarlos.

wonhee
fuente
La adición de esta configuración está funcionando, pero sucede algo extraño: no puedo usar @Value para el tipo de archivo, debo cambiar el archivo con Resource.
ad3luc
2

puedes hacer esto con anotaciones como esta

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

aquí my.list.of.strings se seleccionará del archivo de propiedades, si no está allí, se utilizarán los valores predeterminados a, b, c

y en su archivo de propiedades, puede tener algo como esto

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

verma
fuente
2

Cuidado con los espacios en los valores. Podría estar equivocado, pero creo que los espacios en la lista separada por comas no se truncan usando @Value y Spel. La lista

foobar=a, b, c

se leería como una lista de cadenas

"a", " b", " c"

¡En la mayoría de los casos, probablemente no quieras los espacios!

La expresion

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

te daría una lista de cadenas:

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

La expresión regular elimina todos los espacios justo antes y justo después de una coma. Los espacios dentro de los valores no se eliminan. Entonces

foobar = AA, B B, CCC

debe dar lugar a valores

"AA", "B B", "CCC".
DAMungra
fuente
El método split () utiliza regex internamente como delimitador, por lo que su ejemplo puede simplificarse: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B
2

Creo que esto es más simple para agarrar la matriz y eliminar espacios:

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

En mi caso de una lista de enteros funciona esto:

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

Archivo de propiedad:

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

Considere usar la configuración de Commons. Tiene una función integrada para dividir una entrada en el archivo de propiedades en la matriz / lista. Combinar con SpEL y @Value debería darte lo que quieres


Según lo solicitado, esto es lo que necesita (Realmente no he probado el código, puede tener algunos errores tipográficos, tenga paciencia conmigo):

En la configuración de Apache Commons, hay propiedades de configuración. Admite la función de convertir cadenas delimitadas a matriz / lista.

Por ejemplo, si tiene un archivo de propiedades

#Foo.properties
foo=bar1, bar2, bar3

Con el siguiente código:

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

le dará una serie de cadenas de ["bar1", "bar2", "bar3"]

Para usar con Spring, tenga esto en el contexto de su aplicación xml:

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

y ten esto en tu haba de primavera:

public class SomeBean {

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

Creo que esto debería funcionar: P

Adrian Shum
fuente
¿podría ser más específico sobre el método y la clase que usaría, y el fragmento de código de muestra real sería genial?
JackDev
1

Mi forma preferida (para cadenas, en particular), es la siguiente:

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

y use

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

De esta manera, puede incluir también comas en su parámetro. Funciona también para conjuntos.

Sampisa
fuente
0

si usa marcadores de posición de propiedad, entonces el ejemplo ser1702544 se convertiría en

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

Con marcador de posición 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> 
gaviota
fuente
0

Estoy usando Spring Boot 2.2.6

Mi archivo de propiedad:

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

Mi código:

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

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

Funciona bien

Ankit
fuente