java - use - string @value




Como posso injetar um valor de propriedade em um Spring Bean que foi configurado usando anotações? (12)

Antes de chegarmos ao Spring 3 - que permite que você injete propriedades constantes diretamente em seus beans usando anotações - eu escrevi uma subclasse do bean PropertyPlaceholderConfigurer que faz a mesma coisa. Então, você pode marcar seus setters de propriedades e o Spring irá inserir suas propriedades em seus beans da seguinte forma:

@Property(key="property.key", defaultValue="default")
public void setProperty(String property) {
    this.property = property;
}

A anotação é a seguinte:

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Property {
    String key();
    String defaultValue() default "";
}

O PropertyAnnotationAndPlaceholderConfigurer é o seguinte:

public class PropertyAnnotationAndPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private static Logger log = Logger.getLogger(PropertyAnnotationAndPlaceholderConfigurer.class);

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) throws BeansException {
        super.processProperties(beanFactory, properties);

        for (String name : beanFactory.getBeanDefinitionNames()) {
            MutablePropertyValues mpv = beanFactory.getBeanDefinition(name).getPropertyValues();
            Class clazz = beanFactory.getType(name);

            if(log.isDebugEnabled()) log.debug("Configuring properties for bean="+name+"["+clazz+"]");

            if(clazz != null) {
                for (PropertyDescriptor property : BeanUtils.getPropertyDescriptors(clazz)) {
                    Method setter = property.getWriteMethod();
                    Method getter = property.getReadMethod();
                    Property annotation = null;
                    if(setter != null && setter.isAnnotationPresent(Property.class)) {
                        annotation = setter.getAnnotation(Property.class);
                    } else if(setter != null && getter != null && getter.isAnnotationPresent(Property.class)) {
                        annotation = getter.getAnnotation(Property.class);
                    }
                    if(annotation != null) {
                        String value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
                        if(StringUtils.isEmpty(value)) {
                            value = annotation.defaultValue();
                        }
                        if(StringUtils.isEmpty(value)) {
                            throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
                        }
                        if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+property.getName()+"] value=["+annotation.key()+"="+value+"]");
                        mpv.addPropertyValue(property.getName(), value);
                    }
                }

                for(Field field : clazz.getDeclaredFields()) {
                    if(log.isDebugEnabled()) log.debug("examining field=["+clazz.getName()+"."+field.getName()+"]");
                    if(field.isAnnotationPresent(Property.class)) {
                        Property annotation = field.getAnnotation(Property.class);
                        PropertyDescriptor property = BeanUtils.getPropertyDescriptor(clazz, field.getName());

                        if(property.getWriteMethod() == null) {
                            throw new BeanConfigurationException("setter for property=["+clazz.getName()+"."+field.getName()+"] not available.");
                        }

                        Object value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
                        if(value == null) {
                            value = annotation.defaultValue();
                        }
                        if(value == null) {
                            throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
                        }
                        if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+field.getName()+"] value=["+annotation.key()+"="+value+"]");
                        mpv.addPropertyValue(property.getName(), value);
                    }
                }
            }
        }
    }

}

Sinta-se à vontade para modificar a gosto

Eu tenho um monte de beans Spring que são apanhados a partir do classpath via anotações, por exemplo

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {
    // Implementation omitted
}

No arquivo XML Spring, há um PropertyPlaceholderConfigurer definido:

<bean id="propertyConfigurer" 
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/WEB-INF/app.properties" />
</bean> 

Eu quero injetar uma das propriedades de app.properites no bean mostrado acima. Eu não posso simplesmente fazer algo como

<bean class="com.example.PersonDaoImpl">
    <property name="maxResults" value="${results.max}"/>
</bean>

Porque PersonDaoImpl não está no arquivo XML do Spring (ele é selecionado a partir do classpath via anotações). Eu tenho até o seguinte:

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {

    @Resource(name = "propertyConfigurer")
    protected void setProperties(PropertyPlaceholderConfigurer ppc) {
    // Now how do I access results.max? 
    }
}

Mas não está claro para mim como eu acesso a propriedade que estou interessado em ppc ?


Caminho da primavera:
private @Value("${propertyName}") String propertyField;

é uma nova maneira de injetar o valor usando a classe "PropertyPlaceholderConfigurer" do Spring. Outra maneira é ligar

java.util.Properties props = System.getProperties().getProperty("propertyName");

Nota: Para @Value, você não pode usar o propertyField estático , ele deve ser não-estático somente, caso contrário, ele retorna null. Para corrigi-lo, um setter não estático é criado para o campo estático e o @Value é aplicado acima desse setter.


Eu acho que é a maneira mais conveniente de injetar propriedades no método setter do bean.

Exemplo:

package org.some.beans;

public class MyBean {
    Long id;
    String name;

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Feijão xml definição:

<bean id="Bean1" class="org.some.beans.MyBean">
    <property name="id" value="1"/>
    <property name="name" value="MyBean"/>
</bean>

Para cada método de property nomeado, setProperty(value) será chamado.

Essa maneira é especialmente útil se você precisar de mais de um bean com base em uma implementação.

Por exemplo, se definirmos mais um bean em xml:

<bean id="Bean2" class="org.some.beans.MyBean">
    <property name="id" value="2"/>
    <property name="name" value="EnotherBean"/>
</bean>

Então codifique como este:

MyBean b1 = appContext.getBean("Bean1");
System.out.println("Bean id = " + b1.getId() + " name = " + b1.getName());
MyBean b2 = appContext.getBean("Bean2");
System.out.println("Bean id = " + b2.getId() + " name = " + b2.getName());

Vai imprimir

Bean id = 1 name = MyBean
Bean id = 2 name = AnotherBean

Então, no seu caso, deve ficar assim:

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {

    Long maxResults;

    public void setMaxResults(Long maxResults) {
        this.maxResults = maxResults;
    }

    // Now use maxResults value in your code, it will be injected on Bean creation
    public void someMethod(Long results) {
        if (results < maxResults) {
            ...
        }
    }
}

Eu preciso ter dois arquivos de propriedades, um para produção e um override para desenvolvimento (que não será implantado).

Para ter ambos, um Bean de Propriedades que pode ser autowired e um PropertyConfigurer, você pode escrever:

<bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="singleton" value="true" />

    <property name="ignoreResourceNotFound" value="true" />
    <property name="locations">
        <list>
            <value>classpath:live.properties</value>
            <value>classpath:development.properties</value>
        </list>
    </property>
</bean>

e faça referência ao Bean de Propriedades no PropertyConfigurer

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties" ref="appProperties" />
</bean>

Outra alternativa é adicionar o bean appProperties mostrado abaixo:

<bean id="propertyConfigurer"   
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="/WEB-INF/app.properties" />
</bean> 


<bean id="appProperties" 
          class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="singleton" value="true"/>

        <property name="properties">
                <props>
                        <prop key="results.max">${results.max}</prop>
                </props>
        </property>
</bean>

Quando recuperado, esse bean pode ser convertido em um java.util.Properties que conterá uma propriedade denominada results.max cujo valor é lido de app.properties . Novamente, esse bean pode ser injetado com dependência (como uma instância de java.util.Properties) em qualquer classe por meio da anotação @Resource.

Pessoalmente, eu prefiro essa solução (à outra que propus), pois você pode limitar exatamente quais propriedades são expostas por appProperties e não precisa ler app.properties duas vezes.


Para mim, foi a resposta da @Lucky e, especificamente, a linha

AutowiredFakaSource fakeDataSource = ctx.getBean(AutowiredFakaSource.class);

da página do Captain Debug

isso resolveu meu problema. Eu tenho um aplicativo baseado em ApplicationContext executando a partir da linha de comando e, a julgar por um número de comentários sobre SO, Spring conecta-se de forma diferente aos aplicativos baseados em MVC.


Se você está preso usando o Spring 2.5, você pode definir um bean para cada uma de suas propriedades e injetá-las usando qualificadores. Como isso:

  <bean id="someFile" class="java.io.File">
    <constructor-arg value="${someFile}"/>
  </bean>

e

@Service
public class Thing
      public Thing(@Qualifier("someFile") File someFile) {
...

Não é super legível, mas faz o trabalho.


Se você precisar de mais flexibilidade para as configurações, tente o Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

Em nossa aplicação, usamos:

  • Preferências para configurar o sistema PreProd e Prod
  • Preferências e variáveis ​​de ambiente JNDI (o JNDI sobrescreve as preferências) para "mvn jetty: run"
  • Propriedades do sistema para UnitTests (anotação @BeforeClass)

A ordem padrão em que o valor-chave-Fonte é verificado primeiro é descrita em:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Pode ser personalizado com um settings4j.xml (preciso para log4j.xml) no seu classpath.

Deixe-me saber sua opinião: [email protected]

com saudações amigáveis,
Harald


Use a classe "PropertyPlaceholderConfigurer" do Spring

Um exemplo simples mostrando o arquivo de propriedades lido dinamicamente como propriedade do bean

<bean id="placeholderConfig"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>/WEB-INF/classes/config_properties/dev/database.properties</value>
        </list>
    </property> 
</bean>

<bean id="devDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${dev.app.jdbc.driver}"/>
    <property name="jdbcUrl" value="${dev.app.jdbc.url}"/>
    <property name="user" value="${dev.app.jdbc.username}"/>
    <property name="password" value="${dev.app.jdbc.password}"/>
    <property name="acquireIncrement" value="3"/>
    <property name="minPoolSize" value="5"/>
    <property name="maxPoolSize" value="10"/>
    <property name="maxStatementsPerConnection" value="11000"/>
    <property name="numHelperThreads" value="8"/>
    <property name="idleConnectionTestPeriod" value="300"/>
    <property name="preferredTestQuery" value="SELECT 0"/>
</bean> 

Arquivo de Propriedade

dev.app.jdbc.driver = com.mysql.jdbc.Driver

dev.app.jdbc.url = jdbc: mysql: // localhost: 3306 / addvertisement

dev.app.jdbc.username = root

dev.app.jdbc.password = root


Você pode fazer isso na primavera 3 usando o suporte EL. Exemplo:

@Value("#{systemProperties.databaseName}")
public void setDatabaseName(String dbName) { ... }

@Value("#{strategyBean.databaseKeyGenerator}")
public void setKeyGenerator(KeyGenerator kg) { ... }

systemProperties é um objeto implícito e strategyBean é um nome de bean.

Mais um exemplo, que funciona quando você quer pegar uma propriedade de um objeto Properties . Também mostra que você pode aplicar @Value aos campos:

@Value("#{myProperties['github.oauth.clientId']}")
private String githubOauthClientId;

Aqui está uma postagem no blog que escrevi sobre isso para um pouco mais de informação.


<context:property-placeholder ... /> é o equivalente XML ao PropertyPlaceholderConfigurer.

Exemplo: applicationContext.xml

<context:property-placeholder location="classpath:test.properties"/>  

Classe de componente

 private @Value("${propertyName}") String propertyField;

Autowiring Property Values ​​em Spring Beans:

A maioria das pessoas sabe que você pode usar o @Autowired para informar ao Spring para injetar um objeto em outro quando ele carregar o contexto do seu aplicativo. Um nugget de informação menos conhecido é que você também pode usar a anotação @Value para injetar valores de um arquivo de propriedades nos atributos de um bean. veja este post para mais informações ...

coisas novas na Primavera 3.0 || valores de bean de autowiring || valores de propriedade de autowiring na primavera





dependency-injection