148 votes

Accéder au fichier de propriétés de manière programmatique avec Spring ?

Nous utilisons le code ci-dessous pour injecter des propriétés de Spring beans à partir d'un fichier de propriétés.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:/my.properties"/>
</bean>

<bean id="blah" class="abc">
    <property name="path" value="${the.path}"/>
</bean>

Existe-t-il un moyen d'accéder aux propriétés par programme ? J'essaie de faire du code sans injection de dépendance. J'aimerais donc avoir un code comme celui-ci :

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");

0 votes

Un exemple complet d'accès à un fichier de propriétés dans Spring se trouve sur le lien suivant : bharatonjava.wordpress.com/2012/08/24/

196voto

skaffman Points 197885

Pourquoi pas PropertiesLoaderUtils ?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);

6 votes

Voici une question, en quoi est-elle différente de la mienne, et a deux votes de plus ET a été postée en deuxième position...

3 votes

Je ne sais pas, je n'ai pas pu voter :) Je n'utiliserais pas de PropertyPlaceholderConfigurer Bien qu'il s'agisse d'un outil surdimensionné par rapport à la tâche à accomplir, il n'en reste pas moins qu'il n'est pas nécessaire.

5 votes

J'essayais de me rapprocher le plus possible de ce qu'il avait, j'ai été downvoted tellement de fois pour ne pas avoir fourni assez de détails. En tout cas, vos réponses méritent les votes, car elles sont correctes, je pense que je suis juste jaloux de ne pas avoir eu 2 votes aussi, LOL.

61voto

anttix Points 3470

Si tout ce que vous voulez faire est d'accéder à la valeur de l'espace réservé à partir du code, il y a l'option @Value annotation :

@Value("${settings.some.property}")
String someValue;

Pour accéder aux espaces réservés à partir de SPEL, utilisez la syntaxe suivante :

#('${settings.some.property}')

Pour exposer la configuration aux vues pour lesquelles SPEL est désactivé, il est possible d'utiliser cette astuce :

package com.my.app;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {  
    ConfigurableBeanFactory beanFactory; 

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }

    protected String resolveProperty(String name) {
        String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");

        return rv;
    }

    @Override
    public String get(Object key) {
        return resolveProperty(key.toString());
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            resolveProperty(key.toString());
            return true;
        }
        catch(Exception e) {
            return false;
        }
    }

    @Override public boolean isEmpty() { return false; }
    @Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
    @Override public Collection<String> values() { throw new UnsupportedOperationException(); }
    @Override public int size() { throw new UnsupportedOperationException(); }
    @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}

Puis utiliser l'exposant pour exposer les propriétés à une vue :

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    <property name="attributesMap">
        <map>
            <entry key="config">
                <bean class="com.my.app.PropertyPlaceholderExposer" />
            </entry>
        </map>
    </property>
</bean>

Ensuite, dans la vue, utilisez les propriétés exposées comme suit :

${config['settings.some.property']}

Cette solution présente l'avantage de pouvoir s'appuyer sur des caractères génériques standard. injectée par la balise context:property-placeholder.

Enfin, si vous avez vraiment besoin de capturer toutes les propriétés placeholder et leurs valeurs, vous devez les faire passer par StringValueResolver pour vous assurer que les placeholders fonctionnent à l'intérieur des valeurs de la propriété comme prévu. Le code suivant permet de le faire.

package com.my.app;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;

public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {

    Map<String, String> props = new HashMap<String, String>();

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {

        this.props.clear();
        for (Entry<Object, Object> e: props.entrySet())
            this.props.put(e.getKey().toString(), e.getValue().toString());

        super.processProperties(beanFactory, props);
    }

    @Override
    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            StringValueResolver valueResolver) {

        super.doProcessProperties(beanFactoryToProcess, valueResolver);

        for(Entry<String, String> e: props.entrySet())
            e.setValue(valueResolver.resolveStringValue(e.getValue()));
    }

    // Implement map interface to access stored properties
    @Override public Set<String> keySet() { return props.keySet(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
    @Override public Collection<String> values() { return props.values(); }
    @Override public int size() { return props.size(); }
    @Override public boolean isEmpty() { return props.isEmpty(); }
    @Override public boolean containsValue(Object value) { return props.containsValue(value); }
    @Override public boolean containsKey(Object key) { return props.containsKey(key); }
    @Override public String get(Object key) { return props.get(key); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}

0 votes

Merci pour cette réponse très complète ! Existe-t-il un moyen de faire cela avec des champs finaux ?

2 votes

@WardC vous ne pouvez pas injecter dans un champ final. Cependant, vous pouvez injecter dans un argument du constructeur et définir une valeur de champ final à l'intérieur du constructeur. Voir stackoverflow.com/questions/2306078/ y stackoverflow.com/questions/4203302/

0 votes

J'ai essayé la méthode SpEL, mais elle ne donne rien. 'Invalid collection name: #('${producer.mongodb.collection}')' . J'ai configuré comme : @Document(collection = "#('${producer.mongodb.collection}')")

51voto

Kalinga Points 497

CRÉDIT : Accès programmatique aux propriétés dans Spring sans relire le fichier de propriétés

J'ai trouvé une bonne implémentation pour accéder aux propriétés de manière programmatique dans Spring sans recharger les mêmes propriétés que Spring a déjà chargées. [De plus, il n'est pas nécessaire de coder en dur l'emplacement du fichier de propriétés dans la source.]

Grâce à ces changements, le code est plus propre et plus facile à maintenir.

Le concept est assez simple. Il suffit d'étendre le conteneur de propriétés par défaut de Spring (PropertyPlaceholderConfigurer) et de capturer les propriétés qu'il charge dans la variable locale

public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {

    private static Map<String, String> propertiesMap;
    // Default as in PropertyPlaceholderConfigurer
    private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;

    @Override
    public void setSystemPropertiesMode(int systemPropertiesMode) {
        super.setSystemPropertiesMode(systemPropertiesMode);
        springSystemPropertiesMode = systemPropertiesMode;
    }

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

        propertiesMap = new HashMap<String, String>();
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
            propertiesMap.put(keyStr, valueStr);
        }
    }

    public static String getProperty(String name) {
        return propertiesMap.get(name).toString();
    }

}

Exemple d'utilisation

SpringPropertiesUtil.getProperty("myProperty")

Changements dans la configuration de Spring

<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="locations">
    <list>
        <value>classpath:myproperties.properties</value>
    </list>
    </property>
</bean>

J'espère que cela vous aidera à résoudre vos problèmes

9 votes

Il ne s'agit pas d'une mise en œuvre complète et elle ne fonctionnera pas correctement. Le PropertyPlaceholderConfigurer utilise un PropertyPlaceholderHelper pour remplacer TOUTES les propriétés des placeholders, y compris les placeholders imbriqués. Dans l'implémentation de Kalinga, si vous avez quelque chose comme myFile=${myFolder}/myFile.txt, la valeur littérale de la propriété que vous obtiendrez de la carte en utilisant la clé "myFile" sera ${myFolder}/myFile.txt.

1 votes

C'est la bonne solution. Pour répondre à la question de Brian. La propriété ${myFolder} devrait être une propriété système et ne pas se trouver dans le fichier properties. Cela peut être résolu en définissant la propriété système de tomcat ou la propriété d'exécution dans eclipse. Vous pourriez même avoir une propriété de construction. Cette solution suppose un peu cela et devrait y remédier, mais en même temps cette réponse est beaucoup plus dans la ligne de la pratique standard de charger les propriétés spring et java en un seul endroit au lieu de les charger séparément. Une autre option est de charger un fichier de propriétés générales avec myFile dans le fichier et de l'utiliser pour obtenir le reste.

2 votes

J'ai essayé d'appliquer cette solution au "nouveau" PropertySourcesPlaceholderConfigurer de Spring 3.1+ mais j'ai découvert que la méthode processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) est maintenant dépréciée et qu'il n'y a donc plus d'accès à l'argument "props". En regardant les sources de PropertySourcesPlaceholderConfigurer, je n'ai pas trouvé de moyen propre d'exposer les propriétés. Une idée pour le faire ? Merci de votre compréhension.

51voto

Zoidberg Points 5656

C'est ce que j'ai fait et cela a fonctionné.

Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);

Cela devrait fonctionner.

28voto

enkor Points 1593

Vous pouvez également utiliser les utilitaires Spring ou charger des propriétés via le PropertiesFactoryBean.

<util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>

ou :

<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="classpath:com/foo/myprops.properties"/>
</bean>

Vous pouvez ensuite les récupérer dans votre dossier de candidature :

@Resource(name = "myProps")
private Properties myProps;

et utilisez également ces propriétés dans votre configuration :

<context:property-placeholder properties-ref="myProps"/>

Cela figure également dans la documentation : http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X