Accéder au fichier de propriétés par programme avec Spring?

137

Nous utilisons le code ci-dessous pour injecter des beans Spring avec des propriétés à 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'essaye de faire du code sans injection de dépendance. Alors j'aimerais juste avoir un code comme celui-ci:

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
Marcus Léon
la source
Un exemple complet d'accès au fichier de propriétés au printemps se trouve au lien suivant: bharatonjava.wordpress.com/2012/08/24

Réponses:

171

Qu'en est-il de PropertiesLoaderUtils ?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
skaffman
la source
5
voici une question, en quoi est-ce différent de la mienne, et a encore deux votes ET posté deuxième ...
Zoidberg
3
Beats me, je n'ai pas pu voter :) Je n'utiliserais pas un PropertyPlaceholderConfigurer, même si c'est exagéré pour la tâche.
skaffman
5
J'essayais de me rapprocher le plus possible de ce qu'il avait, j'ai été critiqué tant de fois pour ne pas avoir fourni suffisamment de détails. En tout cas, vos réponses méritent les votes, car c'est correct, je suppose que je suis juste jaloux de ne pas avoir eu 2 votes aussi, LOL.
Zoidberg
1
que devons-nous donner dans le chemin si le fichier est placé dans un répertoire externe, disons le dossier de configuration?
prnjn
52

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' @Valueannotation:

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

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

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

Pour exposer la configuration aux vues sur lesquelles SPEL est désactivé, on peut 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(); }
}

Et puis utilisez l'exposeur 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>

Alors en vue, utilisez les propriétés exposées comme ceci:

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

Cette solution présente l'avantage de pouvoir compter sur une implémentation d'espace réservé standard injectée par la balise context: property-placeholder.

Maintenant, pour terminer, si vous avez vraiment besoin d'un pour capturer toutes les propriétés d'espace réservé et leurs valeurs, vous devez les diriger via StringValueResolver pour vous assurer que les espaces réservés fonctionnent comme prévu dans les valeurs de propriété. Le code suivant le fera.

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(); }
}
anttix
la source
Merci pour cette réponse très complète! y a-t-il un moyen de faire cela avec les champs finaux?
Quartier
2
@WardC vous ne pouvez pas injecter dans un champ final. Cependant, vous pouvez injecter un argument de constructeur et définir une valeur de champ finale à l'intérieur du constructeur. Voir stackoverflow.com/questions/2306078/… et stackoverflow.com/questions/4203302/…
anttix
50

CRÉDIT : accès par programme aux propriétés dans Spring sans relire le fichier de propriétés

J'ai trouvé une belle implémentation pour accéder aux propriétés par programme au printemps 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]

Avec ces changements, le code semble plus propre et plus facile à gérer.

le concept est assez simple. Il suffit d'étendre l'espace réservé aux propriétés par défaut du ressort (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 de configuration de printemps

<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 aidera à résoudre les problèmes que vous avez

Ashok Koyi
la source
8
Ce n'est pas une implémentation complète et ne fonctionnera pas correctement. PropertyPlaceholderConfigurer utilise un PropertyPlaceholderHelper pour remplacer TOUTES les propriétés d'espace réservé, y compris les espaces réservés imbriqués. Dans l'implémentation de Kalinga, si vous avez quelque chose comme myFile = $ {myFolder} /myFile.txt, la valeur de propriété littérale que vous obtiendrez de la carte en utilisant la clé "myFile" sera $ {myFolder} /myFile.txt.
1
C'est la bonne solution. Pour répondre à l'inquiétude de Brian. Le $ {myFolder} doit être une propriété système et ne pas être dans le fichier de propriétés. Cela peut être résolu en définissant la propriété système tomcat ou la propriété run dans eclipse. Vous pourriez même avoir une propriété de construction. Cette solution suppose qu'un peu et devrait aborder cela, mais en même temps, cette réponse est beaucoup plus proche de la pratique standard pour charger les propriétés spring et java en un seul endroit plutôt que séparément. Une autre option consiste à charger un fichier de propriétés générales avec myFile dans le fichier et à l'utiliser pour obtenir le reste.
Rob
1
J'ai essayé d'appliquer cette solution de contournement au `` nouveau '' PropertySourcesPlaceholderConfigurer de Spring 3.1+, mais j'ai trouvé que la méthode processProperties (ConfigurableListableBeanFactory beanFactory, Propriétés props) est désormais obsolète et qu'il n'y a donc plus d'accès à l'argument `` props ''. L'examen des sources de PropertySourcesPlaceholderConfigurer ne peut pas trouver un moyen propre d'exposer les propriétés. Des idées pour le faire? Merci!
Jorge Palacio
48

J'ai fait cela et cela a fonctionné.

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

Cela devrait fonctionner.

Zoidberg
la source
25

Vous pouvez également utiliser les utilitaires de printemps 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>

Ensuite, vous pouvez les récupérer dans votre application avec:

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

et utilisez en plus ces propriétés dans votre configuration:

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

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

enkor
la source
10

Créez une classe comme ci-dessous

    package com.tmghealth.common.util;

    import java.util.Properties;

    import org.springframework.beans.BeansException;

    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

    import org.springframework.context.annotation.Configuration;

    import org.springframework.context.annotation.PropertySource;

    import org.springframework.stereotype.Component;


    @Component
    @Configuration
    @PropertySource(value = { "classpath:/spring/server-urls.properties" })
    public class PropertiesReader extends PropertyPlaceholderConfigurer {

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

        }

    }

Puis là où vous voulez accéder à une utilisation immobilière

    @Autowired
        private Environment environment;
    and getters and setters then access using 

    environment.getProperty(envName
                    + ".letter.fdi.letterdetails.restServiceUrl");

- écrire des getters et des setters dans la classe accesseur

    public Environment getEnvironment() {
            return environment;
        }`enter code here`

        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
user1419261
la source
1
De loin la meilleure réponse, devrait juste autowire Environnement.
sbochins du
4

Comme vous le savez, les versions plus récentes de Spring n'utilisent pas PropertyPlaceholderConfigurer et utilisent maintenant une autre construction cauchemardesque appelée PropertySourcesPlaceholderConfigurer. Si vous essayez d'obtenir des propriétés résolues à partir du code et que vous souhaitez que l'équipe Spring nous donne un moyen de le faire il y a longtemps, votez pour ce message! ... Parce que c'est ainsi que vous procédez de la nouvelle façon:

PropertySourcesPlaceholderConfigurer de sous-classe:

public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {

    private ConfigurableListableBeanFactory factory;

    /**
     * Save off the bean factory so we can use it later to resolve properties
     */
    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            final ConfigurablePropertyResolver propertyResolver) throws BeansException {
        super.processProperties(beanFactoryToProcess, propertyResolver);

        if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
            logger.debug("Value resolver exists.");
            factory = beanFactoryToProcess;
        }
        else {
            logger.error("No existing embedded value resolver.");
        }
    }

    public String getProperty(String name) {
        Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
        return propertyValue.toString();
    }
}

Pour l'utiliser, assurez-vous d'utiliser votre sous-classe dans votre @Configuration et enregistrez-y une référence pour une utilisation ultérieure.

@Configuration
@ComponentScan
public class PropertiesConfig {

    public static SpringPropertyExposer commonEnvConfig;

    @Bean(name="commonConfig")
    public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
        commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
        PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
        commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
        try {
            commonConfig.afterPropertiesSet();
        }
        catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
        commonEnvConfig.setProperties(commonConfig.getObject());
        return commonEnvConfig;
    }
}

Usage:

Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
TheJeff
la source
2

Voici un autre exemple.

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);
Venky
la source
2

Cela m'aide à:

ApplicationContextUtils.getApplicationContext().getEnvironment()
Ruzal Yumaev
la source
Quel package est ApplicationContextUtils dans
Luke
2

Cela résoudra toutes les propriétés imbriquées.

public class Environment extends PropertyPlaceholderConfigurer {

/**
 * Map that hold all the properties.
 */
private Map<String, String> propertiesMap; 

/**
 * Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
 */
@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 = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
        propertiesMap.put(keyStr, valueStr);
    }
} 

/**
 * This method gets the String value for a given String key for the property files.
 * 
 * @param name - Key for which the value needs to be retrieved.
 * @return Value
 */
public String getProperty(String name) {
    return propertiesMap.get(name).toString();
}
Sohan
la source
2

Vous pouvez obtenir vos propriétés en Environmentclasse. Dans l'état actuel de la documentation:

Les propriétés jouent un rôle important dans presque toutes les applications et peuvent provenir de diverses sources: fichiers de propriétés, propriétés système JVM, variables d'environnement système, JNDI, paramètres de contexte de servlet, objets Propriétés ad-hoc, cartes, etc. Le rôle de l'objet d'environnement par rapport aux propriétés est de fournir à l'utilisateur une interface de service pratique pour configurer les sources de propriétés et en résoudre les propriétés.

Ayant Environment comme envvariable, appelez simplement:

env.resolvePlaceholders("${your-property:default-value}")

Vous pouvez obtenir vos propriétés `` brutes '' via:

env.getProperty("your-property")

Il recherchera dans toutes les sources de propriétés enregistrées par le printemps.

Vous pouvez soit obtenir l'environnement via:

  • injecter ApplicationContext en implémentant ApplicationContextAwarepuis en appelant getEnvironment()le contexte
  • mettre en œuvre EnvironmentAware.

Il est obtenu via l'implémentation d'une classe car les propriétés sont résolues au début du démarrage de l'application, car elles peuvent être nécessaires pour la construction du bean.

En savoir plus sur la documentation: documentation Spring Environment

Augustin Ghauratto
la source
1

Cet article explique également comment accéder aux propriétés: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

Vous pouvez accéder aux propriétés chargées par spring property-placeholder sur un tel spring bean:

@Named
public class PropertiesAccessor {

    private final AbstractBeanFactory beanFactory;

    private final Map<String,String> cache = new ConcurrentHashMap<>();

    @Inject
    protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public  String getProperty(String key) {
        if(cache.containsKey(key)){
            return cache.get(key);
        }

        String foundProp = null;
        try {
            foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
            cache.put(key,foundProp);
        } catch (IllegalArgumentException ex) {
           // ok - property was not found
        }

        return foundProp;
    }
}
Maciej Miklas
la source
0
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`

dans servlet-context.xml après cela, vous pouvez directement utiliser votre fichier partout

Aniket
la source
0

Veuillez utiliser le code ci-dessous dans votre fichier de configuration Spring pour charger le fichier à partir du chemin de classe de votre application

 <context:property-placeholder
    ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
Abhishek Jha
la source
0

C'est la meilleure façon dont je l'ai fait fonctionner:

package your.package;

import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

public class ApplicationProperties {

    private Properties properties;

    public ApplicationProperties() {
        // application.properties located at src/main/resource
        Resource resource = new ClassPathResource("/application.properties");
        try {
            this.properties = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getProperty(String propertyName) {
        return this.properties.getProperty(propertyName);
    }
}
Daniel Almeida
la source
Instanciez la classe et appelez la méthode obj.getProperty ("my.property.name");
Daniel Almeida