Ler uma lista do arquivo de propriedades e carregar com a anotação de mola @Value

244

Eu quero ter uma lista de valores em um arquivo .properties, ou seja:

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

E para carregá-lo diretamente na minha classe, ou seja:

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

Pelo que entendi, uma alternativa de fazer isso é tê-lo no arquivo de configuração da primavera e carregá-lo como uma referência de bean (me corrija se estiver errado), ou seja,

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

Mas existe alguma maneira de fazer isso? usando um arquivo .properties? ps: eu gostaria de fazer isso sem nenhum código personalizado, se possível.

JackDev
fonte

Respostas:

453

Usando o Spring EL:

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

Supondo que seu arquivo de propriedades seja carregado corretamente com o seguinte:

my.list.of.strings=ABC,CDE,EFG
Wilhelm Kleu
fonte
1
Eu verifiquei novamente usando a mesma versão que você está usando. Copiou o Spring EL exatamente como no post e funciona. O que é diferente, porém, é que, se eu cometer um erro no meu EL, recebo uma org.springframework.expression.spel.SpelEvaluationExceptionexceção e não javax.el.ELException. Seu objeto foi criado pelo Spring?
Wilhelm Kleu 25/10/12
1
Funciona perfeitamente com o Spring 3.2.
ehsanullahjan
17
E quanto à propriedade vazia my.list.of.strings=? Eu esperaria que essa funcionalidade recuperasse a lista vazia, onde aqui estará um item (string vazia), não?
Jan Zyka
5
Observe também que a solução sugerida não faz o corte; portanto, valores como item1, item2, item3podem dar o resultado que você realmente não espera (dica: espaços).
Jan Zyka
4
Se você precisa cortar espaços vazios usar regex para o argumento de divisão .. algo como@Value("#{'${my.list.of.strings}'.split(',\\s*')}")
Stackee007
88

Desde o Spring 3.0, você pode adicionar uma linha como

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

ao seu applicationContext.xml(ou onde você configura as coisas). Como Dmitry Chornyi aponta em um comentário, a configuração baseada em Java se parece com:

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

Isso ativa o novo serviço de configuração que suporta a conversão Stringem Collectiontipos. Se você não ativar esse serviço de configuração, o Spring recorrerá a seus editores de propriedades herdados como serviços de configuração, que não suportam esse tipo de conversão.

A conversão para coleções de outros tipos também funciona:

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

funcionará com uma linha como

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

Não há problemas com espaço em branco lá, ConversionServiceFactoryBeanele cuida disso.

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

Em um aplicativo Spring, você normalmente configura uma instância ConversionService por contêiner Spring (ou ApplicationContext). Esse ConversionService será escolhido pelo Spring e depois usado sempre que uma conversão de tipo precisar ser executada pela estrutura. [...] Se nenhum ConversionService estiver registrado no Spring, o sistema baseado no PropertyEditor original será usado.

Klein-Robbenhaar, Clemens
fonte
6
Configuração Java: @Bean public ConversionService conversionService () {return new DefaultConversionService (); }
Dmitry Chornyi 19/11/2015
2
Fora evitando repetir-se com split()em cada expressão, ele também lida adequadamente uma lista vazia em vez de dar-lhe[null]
Didier L
Ele não está funcionando se a propriedade é overrided (existe em várias fontes de propriedade.)
Daniel Hári
47

Se você está lendo isso e está usando o Spring Boot , tem mais 1 opção para esse recurso

Geralmente, a lista separada por vírgula é muito desajeitada para o caso de uso do mundo real (e às vezes nem é possível, se você quiser usar vírgulas na sua configuração):

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

Com o Spring Boot , você pode escrevê-lo assim (Índice começa em 0):

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

E use-o assim:

@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
fonte
Tenho um problema em que as outras soluções descritas aqui não funcionam devido a vírgulas vazias que não estão sendo reconhecidas. Existe uma maneira de fazer essa solução com o Spring 4?
Sandrozbinden
34

Especificando o my.list.of.strings=ABC,CDE,EFGarquivo .properties e usando

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

Você pode obter as matrizes de strings. E usando CollectionUtils.addAll(myList, myString), você pode obter a lista de strings.

Mukesh Kumar
fonte
Eu recebo apenas a primeira String "ABC"
Osama Abdulsattar
19

Você considerou @Autowiredo construtor ou o levantador e String.split()o corpo?

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

Costumo preferir fazer minha fiação automática de uma dessas maneiras para melhorar a testabilidade do meu código.

nicholas.hauschild
fonte
14

Se você estiver usando o Spring Boot 2, ele funcionará como está, sem nenhuma configuração adicional.

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

@Value("${my.list.of.strings}")
private List<String> myList;
Bienvenido David
fonte
não funcionou para mim, tenho que usar o Spring EL como mostrado acima.
Bilbo Bolseiro
ou até mesmoprivate List<String> myList;
Halayem Anis 13/12/19
Sim, funcionou para mim também: estou usando o Spring Boot 2.2.6
Ankit
8

Todas as respostas acima estão corretas. Mas você pode conseguir isso em apenas uma linha. Por favor, tente seguir a declaração e você obterá todos os valores separados por vírgula em uma lista de String.

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

E você também precisa ter a seguinte linha definida na sua configuração xml.

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

basta substituir o caminho e o nome do arquivo de propriedades. E você está pronto para ir. :)

Espero que isso ajude você. Felicidades.

Japão Trivedi
fonte
1
Isso funcionou para mim, mas eu tinha que frase a anotação como este:@Value("#{T(java.util.Arrays).asList('${my.list.of.strings}')}")
Steven
6

Se você estiver usando a versão mais recente do framework Spring (Spring 3.1+ eu acredito), não precisará dividir essas coisas no SpringEL,

Basta adicionar PropertySourcesPlaceholderConfigurer e DefaultConversionService na classe de configuração do Spring (aquela anotada com Configuration), por exemplo,

@Configuration
public class AppConfiguration {

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

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

e na sua aula

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

e no arquivo de propriedades

list=A,B,C,D,E

Sem DefaultConversionService, você só pode colocar String separada por vírgula na matriz String quando injetar o valor em seu campo, mas DefaultConversionService faz algumas magias convenientes para você e as adiciona a Collection, Array etc. (verifique a implementação, se desejar gostaria de saber mais sobre isso)

Com esses dois, ele lida com todos os espaços em branco redundantes, incluindo nova linha, para que você não precise adicionar lógicas adicionais para apará-los.

wonhee
fonte
A adição dessa configuração está funcionando, mas algo estranho acontece: não consigo usar o @Value para o tipo de arquivo, devo alterar o arquivo com o recurso.
Ad3luc 6/06/19
2

você pode fazer isso com anotações como esta

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

aqui my.list.of.strings será selecionado no arquivo de propriedades, se não estiver lá, os padrões a, b, c serão usados

e no seu arquivo de propriedades, você pode ter algo parecido com isto

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

verma
fonte
2

Cuidado com os espaços nos valores. Eu posso estar errado, mas acho que os espaços na lista separada por vírgulas não são truncados usando @Value e Spel. A lista

foobar=a, b, c

seria lido como uma lista de strings

"a", " b", " c"

Na maioria dos casos, você provavelmente não gostaria dos espaços!

A expressão

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

daria a você uma lista de strings:

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

A expressão regular remove todos os espaços antes e depois de uma vírgula. Os espaços dentro dos valores não são removidos. assim

foobar = AA, B B, CCC

deve resultar em valores

"AA", "B B", "CCC".
DAMungra
fonte
O método split () usa regex internamente como delimitador, para que seu exemplo possa ser simplificado: <br/>@Value("#{'${foobar}'.trim().split( *, *)}")
Karlik_B
2

Eu acho que isso é mais simples para pegar o array e remover os espaços:

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

No meu caso de uma lista de números inteiros funciona isso:

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

Arquivo de propriedade:

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

Considere usar a Configuração do Commons. Ele foi incorporado ao recurso para quebrar uma entrada no arquivo de propriedades na matriz / lista. Pentear com SpEL e @Value deve dar o que você deseja


Conforme solicitado, eis o que você precisa (ainda não tentei o código, pode ter erros de digitação, tenha paciência comigo):

Na Configuração do Apache Commons, há PropertiesConfiguration. Ele suporta o recurso de conversão de string delimitada em array / lista.

Por exemplo, se você tiver um arquivo de propriedades

#Foo.properties
foo=bar1, bar2, bar3

Com o código abaixo:

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

lhe dará uma série de strings de ["bar1", "bar2", "bar3"]

Para usar com o Spring, faça isso no contexto do aplicativo xml:

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

e coloque isso no seu feijão de primavera:

public class SomeBean {

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

Eu acredito que isso deve funcionar: P

Adrian Shum
fonte
você poderia ser mais específico sobre o método e a classe a usar, e o sniplet de código de amostra real seria ótimo.
21412 JackDev
1

Minha maneira preferida (para strings, em particular), é a seguinte:

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

E use

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

Dessa forma, você também pode incluir vírgulas no seu parâmetro. Também funciona para conjuntos.

Sampisa
fonte
0

se usando espaços reservados de propriedade, o exemplo ser1702544 se tornaria

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

Com espaço reservado 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> 
gaivota
fonte
0

Estou usando o Spring Boot 2.2.6

Meu arquivo de propriedades:

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

Meu código:

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

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

Funciona bem

Ankit
fonte