web-dev-qa-db-fra.com

Comment puis-je injecter une valeur de propriété dans un Spring Bean configuré avec des annotations?

J'ai un groupe de haricots de printemps qui sont extraits du chemin de classe via des annotations, par exemple.

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

Dans le fichier XML Spring, il existe un PropertyPlaceholderConfigurer défini:

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

Je souhaite injecter l’une des propriétés d’app.properites dans le haricot présenté ci-dessus. Je ne peux pas simplement faire quelque chose comme

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

Parce que PersonDaoImpl ne figure pas dans le fichier XML Spring (il est extrait du chemin de classe via des annotations). J'ai aussi loin que ce qui suit:

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

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

Mais je ne sais pas comment accéder à la propriété qui m'intéresse de ppc?

281
Dónal

Vous pouvez le faire au printemps 3 en utilisant le support EL. Exemple:

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

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

systemProperties est un objet implicite et strategyBean est un nom de bean.

Un autre exemple, qui fonctionne lorsque vous souhaitez extraire une propriété d'un objet Properties. Il montre également que vous pouvez appliquer @Value aux champs:

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

Voici un blog post J'ai écrit à ce sujet pour un peu plus d'informations.

285
Willie Wheeler

Personnellement, j'adore cette nouvelle méthode au printemps 3.0 de la documentation :

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

Pas de getters ou setters!

Avec les propriétés en cours de chargement via la config:

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

Pour approfondir ma joie, je peux même contrôler cliquer sur l'expression EL dans IntelliJ et cela m'amène à la définition de la propriété!

Il y a aussi la version totalement non xml :

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

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

Il y a une nouvelle annotation @Value dans Spring 3.0.0M. @Value prend en charge non seulement les expressions #{...}, mais également ${...} les espaces réservés.

120
max

<context:property-placeholder ... /> est l'équivalent XML de PropertyPlaceholderConfigurer.

Exemple: applicationContext.xml

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

Classe de composant

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

Une autre alternative consiste à ajouter le bean appProperties ci-dessous:

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

Une fois récupéré, ce bean peut être converti en un Java.util.Properties qui contiendra une propriété nommée results.max dont la valeur est lue à partir de app.properties. De nouveau, ce bean peut être une dépendance injectée (en tant qu'instance de Java.util.Properties) dans n'importe quelle classe via l'annotation @Resource.

Personnellement, je préfère cette solution (à l'autre que j'ai proposée), car vous pouvez limiter exactement les propriétés exposées par appProperties, sans qu'il soit nécessaire de lire app.properties deux fois.

15
Dónal

J'ai besoin de deux fichiers de propriétés, un pour la production et un remplacement pour le développement (qui ne sera pas déployé).

Pour avoir les deux, un Bean Propriétés qui peut être auto-câblé et un PropertyConfigurer, vous pouvez écrire:

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

et référencez le bean Properties dans PropertyConfigurer

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

Avant d’obtenir Spring 3, qui vous permet d’injecter des constantes de propriété directement dans vos beans à l’aide d’annotations, j’ai écrit une sous-classe du bean PropertyPlaceholderConfigurer qui fait la même chose. Ainsi, vous pouvez marquer vos propriétaires et Spring va automatiquement transférer vos propriétés dans vos haricots de la manière suivante:

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

L'annotation est la suivante:

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

Le PropertyAnnotationAndPlaceholderConfigurer est le suivant:

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

}

N'hésitez pas à modifier à votre goût

7
Ricardo Gladwell

Chemin de printemps:
private @Value("${propertyName}") String propertyField;

est un nouveau moyen d'injecter la valeur à l'aide de la classe "PropertyPlaceholderConfigurer" de Spring. Une autre façon est d'appeler

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

Remarque: Pour @Value, vous ne pouvez pas utiliser statique propertyField, il doit être uniquement non statique, sinon il retourne null. Pour résoudre ce problème, un séparateur non statique est créé pour le champ statique et @Value est appliqué au-dessus de celui-ci.

6
hi.nitish

Vous pouvez également annoter votre classe:

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

Et avoir une variable comme celle-ci:

@Autowired
private Environment env;

Maintenant, vous pouvez accéder à toutes vos propriétés de cette manière:

env.getProperty("database.connection.driver")
6
Alexis Gamarra

Comme mentionné précédemment, @Value fait le travail et il est assez flexible car vous pouvez avoir le printemps EL dedans.

Voici quelques exemples qui pourraient être utiles:

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

Un autre pour obtenir un set d'un 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;

Vous pouvez également définir des valeurs pour les types primitifs.

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

Vous pouvez appeler des méthodes statiques:

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

Vous pouvez avoir de la logique

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

Une solution possible consiste à déclarer un deuxième bean qui lit dans le même fichier de propriétés:

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

Le bean nommé 'appProperties' est de type Java.util.Properties et peut être une dépendance injectée à l'aide de la méthode @Resource présentée ci-dessus.

5
Dónal

Si vous êtes bloqué avec Spring 2.5, vous pouvez définir un haricot pour chacune de vos propriétés et les injecter à l'aide de qualificateurs. Comme ça:

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

et

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

Ce n'est pas très lisible, mais le travail est fait.

3
Nik

Pour moi, c'était la réponse de @ Lucky, et plus précisément, la ligne

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

à partir de la page Captain Debug

cela a résolu mon problème. J'ai une application basée sur ApplicationContext exécutée à partir de la ligne de commande, et à en juger par un certain nombre de commentaires sur SO, Spring les connecte différemment aux applications basées sur MVC.

2
ben3000

Valeurs de propriété de câblage automatique dans beans Spring:

La plupart des gens savent que vous pouvez utiliser @Autowired pour dire à Spring d'injecter un objet dans un autre lors du chargement du contexte de votre application. Un nugget d’information moins connu est que vous pouvez également utiliser l’annotation @Value pour injecter les valeurs d’un fichier de propriétés dans les attributs d’un bean. voir ce post pour plus d'infos ...

nouveautés au printemps 3. || valeurs de bean autowiring || valeurs de propriété autowiring en printemps

2
Lucky

Je pense que le moyen le plus pratique d’injecter des propriétés dans Bean est sa méthode de définition.

Exemple:

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

Définition XML Bean:

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

Pour chaque méthode nommée propertysetProperty(value) sera appelée.

Cette méthode est particulièrement utile si vous avez besoin de plusieurs beans basés sur une implémentation.

Par exemple, si nous définissons un bean supplémentaire dans XML:

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

Puis code comme ceci:

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

Va imprimer

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

Donc, dans votre cas, cela devrait ressembler à ceci:

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

Si vous avez besoin de plus de flexibilité pour les configurations, essayez le Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

Dans notre application, nous utilisons:

  • Préférences pour configurer les systèmes PreProd et Prod
  • Préférences et variables d'environnement JNDI (JNDI écrase les préférences) pour "mvn jetty: run"
  • Propriétés système pour UnitTests (annotation @BeforeClass)

L'ordre par défaut pour lequel clé-valeur-Source est vérifiée en premier est décrit dans:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Il peut être personnalisé avec un settings4j.xml (exact à log4j.xml) dans votre chemin de classe.

Donnez-moi votre opinion: [email protected]

amicalement,
Harald

0
brabenetz