java leer - ¿Cómo puedo inyectar un valor de propiedad en un Spring Bean que se configuró usando anotaciones?




value annotation (15)

Tengo un montón de frijoles de primavera que se recogen de la ruta de clase a través de anotaciones, por ejemplo

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

En el archivo XML de Spring, hay un PropertyPlaceholderConfigurer definido:

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

Quiero inyectar una de las propiedades de app.properites en el bean que se muestra arriba. No puedo simplemente hacer algo como

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

Debido a que PersonDaoImpl no aparece en el archivo XML de Spring (se recoge de la ruta de clase a través de anotaciones). He llegado hasta lo siguiente:

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

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

¿Pero no me queda claro cómo accedo a la propiedad en la que estoy interesado desde ppc ?


Answers

Como se mencionó, @Value hace el trabajo y es bastante flexible ya que puede tener el EL de primavera en él.

Aquí hay algunos ejemplos que podrían ser útiles:

//Build and array from comma separated parameters 
//Like currency.codes.list=10,11,12,13
@Value("#{'${currency.codes.list}'.split(',')}") 
private List<String> currencyTypes;

Otro para obtener un set de una list

//If you have a list of some objects like (List<BranchVO>) 
//and the BranchVO has areaCode,cityCode,...
//You can easily make a set or areaCodes as below
@Value("#{BranchList.![areaCode]}") 
private Set<String> areas;

También puede establecer valores para tipos primitivos.

@Value("${amount.limit}")
private int amountLimit;

Puedes llamar métodos estáticos:

@Value("#{T(foo.bar).isSecurityEnabled()}")
private boolean securityEnabled;

Puedes tener logica

@Value("#{T(foo.bar).isSecurityEnabled() ? '${security.logo.path}' : '${default.logo.path}'}")
private String logoPath;

Antes de obtener Spring 3, que le permite inyectar constantes de propiedades directamente en sus beans mediante anotaciones, escribí una subclase del bean PropertyPlaceholderConfigurer que hace lo mismo. Por lo tanto, puede marcar sus creadores de propiedades y Spring autowire sus propiedades en sus beans como:

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

La anotación es la siguiente:

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

El PropertyAnnotationAndPlaceholderConfigurer es el siguiente:

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);
                    }
                }
            }
        }
    }

}

Siéntase libre de modificar a gusto


Necesito tener dos archivos de propiedades, uno para producción y un reemplazo para el desarrollo (que no se implementará).

Para tener ambos, un bean de propiedades que se puede conectar automáticamente y un PropertyConfigurer, puede escribir:

<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>

y referencia el Bean de Propiedades en el PropertyConfigurer

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

<context:property-placeholder ... /> es el equivalente de XML al PropertyPlaceholderConfigurer.

Ejemplo: applicationContext.xml

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

Clase de componente

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

Hay una nueva anotación @Value en Spring 3.0.0M3 . @Value admite no solo las expresiones #{...} sino también los marcadores de posición ${...}


Otra alternativa es agregar el bean appProperties que se muestra a continuación:

<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>

Cuando se recupera, este bean se puede convertir en un java.util.Properties que contendrá una propiedad llamada results.max cuyo valor se lee desde app.properties . Nuevamente, este bean puede ser una dependencia inyectada (como una instancia de java.util.Properties) en cualquier clase a través de la anotación @Resource.

Personalmente, prefiero esta solución (a la otra que propuse), ya que puede limitar exactamente qué propiedades están expuestas por appProperties y no es necesario leer app.properties dos veces.


También puedes anotar tu clase:

@PropertySource("classpath:/com/myProject/config/properties/database.properties")

Y tener una variable como esta:

@Autowired
private Environment env;

Ahora puedes acceder a todas tus propiedades de esta manera:

env.getProperty("database.connection.driver")

Creo que es la forma más conveniente de inyectar propiedades en el método bean.

Ejemplo:

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;
    }
}

Definición de XML Bean:

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

Para cada property nombre, se invocará setProperty(value) .

Esta forma es especialmente útil si necesita más de un bean basado en una implementación.

Por ejemplo, si definimos un bean más en xml:

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

Entonces codifique así:

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());

Imprimirá

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

Entonces, en tu caso debería verse así:

@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) {
            ...
        }
    }
}

Utilice la clase "PropertyPlaceholderConfigurer" de Spring

Un ejemplo simple que muestra el archivo de propiedades leído dinámicamente como propiedad de 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> 

Archivo de propiedad

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


Si necesita más flexibilidad para las configuraciones, pruebe el Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

En nuestra aplicación utilizamos:

  • Preferencias para configurar el sistema PreProd y Prod.
  • Preferencias y variables de entorno JNDI (JNDI sobrescribe las preferencias) para "mvn jetty: run"
  • Propiedades del sistema para UnitTests (@BeforeClass anotación)

El orden predeterminado en el que la clave-valor-Fuente se verifica primero, se describe en:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Se puede personalizar con un settings4j.xml (exacto a log4j.xml) en su classpath.

Déjame saber tu opinión: [email protected]

con un cordial saludo,
Harald


Manera de primavera:
private @Value("${propertyName}") String propertyField;

es una nueva forma de inyectar el valor utilizando la clase "PropertyPlaceholderConfigurer" de Spring. Otra forma es llamar

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

Nota: para @Value, no puede usar propertyField estático , debe ser solo no estático, de lo contrario devuelve nulo. Para solucionarlo, se crea un definidor no estático para el campo estático y se aplica @Value sobre ese definidor.


Si se queda atascado utilizando Spring 2.5, podría definir un bean para cada una de sus propiedades e inyectarlos utilizando calificadores. Me gusta esto:

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

y

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

No es super legible pero hace el trabajo.


Personalmente me encanta esta nueva forma en la primavera 3.0 de los documentos :

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

No hay captadores o setters!

Con las propiedades cargadas a través de la configuración:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
      p:location="classpath:propertyFile.properties" name="propertiesBean"/>

Para mejorar mi alegría, incluso puedo controlar el clic en la expresión EL en IntelliJ y me lleva a la definición de propiedad.

También está la versión totalmente no xml :

@PropertySource("classpath:propertyFile.properties")
public class AppConfig {

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

Una posible solución es declarar un segundo bean que se lee desde el mismo archivo de propiedades:

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

<util:properties id="appProperties" location="classpath:/WEB-INF/app.properties"/>

El bean denominado 'appProperties' es de tipo java.util.Properties y se puede inyectar la dependencia utilizando el @Resource attruibute que se muestra arriba.


Si va a estar codificado así, solo

private Resource sqlFile = new ClassPathResource("/abc/student/test.sql");

De lo contrario, lo que realmente buscas es

@Value("${some.property}")
private Resource sqlFile;

y creo que al inyectar el valor de la propiedad, se aplicará el PropertyEditor correcto.







java spring dependency-injection