Acessar o arquivo de propriedades programaticamente com o Spring?

137

Usamos o código abaixo para injetar Spring beans com propriedades de um arquivo de propriedades.

<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 uma maneira de acessar as propriedades programaticamente? Estou tentando fazer algum código sem injeção de dependência. Então, eu gostaria de ter apenas um código como este:

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
Marcus Leon
fonte
Um exemplo completo de acesso ao arquivo de propriedades na primavera está no seguinte link: bharatonjava.wordpress.com/2012/08/24/…

Respostas:

171

Que tal PropertiesLoaderUtils ?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
skaffman
fonte
5
aqui é uma pergunta, como isso é diferente do meu, e tem mais dois votos e publicado segundo ...
Zoidberg
3
Bate-me, eu não consegui votar :) Eu não usaria um PropertyPlaceholderConfigurer, embora seja um exagero para a tarefa.
22410 skaffman
5
Eu estava tentando chegar o mais perto possível do que ele tinha; fui criticado tantas vezes por não fornecer detalhes suficientes. De qualquer forma, suas respostas merecem os votos, pois está correto, acho que estou com ciúmes por não ter conseguido 2 votos também, risos.
Zoidberg
1
o que devemos dar no caminho se o arquivo for colocado no diretório externo, digamos pasta de configuração?
prnjn
52

Se tudo o que você deseja fazer é acessar o valor do espaço reservado a partir do código, existe a @Valueanotação:

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

Para acessar espaços reservados no SPEL, use esta sintaxe:

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

Para expor a configuração a visualizações com o SPEL desativado, pode-se usar este truque:

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

E, em seguida, use o expositor para expor propriedades a uma exibição:

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

Em vista, use as propriedades expostas como esta:

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

Esta solução tem a vantagem de poder confiar na implementação padrão de espaço reservado injetada pelo contexto: tag de propriedade-espaço reservado.

Agora, como observação final, se você realmente precisar capturar todas as propriedades de espaço reservado e seus valores, precisará canalizá-las através de StringValueResolver para garantir que os espaços reservados funcionem dentro dos valores da propriedade conforme o esperado. O código a seguir fará isso.

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
fonte
Thnx para esta resposta muito completa! existe uma maneira de fazer isso com os campos finais?
Ward
2
@WardC, você não pode injetar em um campo final. No entanto, você pode injetar um argumento do construtor e definir um valor final do campo dentro do construtor. Consulte stackoverflow.com/questions/2306078/… e stackoverflow.com/questions/4203302/…
anttix
50

CREDIT : acesso programático às propriedades no Spring sem reler o arquivo de propriedades

Eu encontrei uma boa implementação de acessar as propriedades programaticamente na primavera sem recarregar as mesmas propriedades que a primavera já carregou. [Além disso, não é necessário codificar o local do arquivo de propriedades na origem]

Com essas alterações, o código parece mais limpo e mais sustentável.

O conceito é muito simples. Apenas estenda o espaço reservado de propriedade padrão da primavera (PropertyPlaceholderConfigurer) e capture as propriedades que ele carrega na variável local

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

}

Exemplo de uso

SpringPropertiesUtil.getProperty("myProperty")

Mudanças na configuração do 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>

Espero que isso ajude a resolver os problemas que você tem

Ashok Koyi
fonte
8
Esta não é uma implementação completa e não funcionará corretamente. O PropertyPlaceholderConfigurer usa um PropertyPlaceholderHelper para substituir TODAS as propriedades do espaço reservado, incluindo espaços reservados aninhados. Na implementação do Kalinga, se você tiver algo como myFile = $ {myFolder} /myFile.txt, o valor da propriedade literal que você obterá do mapa usando a chave "myFile" será $ {myFolder} /myFile.txt.
1
Esta é a solução correta. Para abordar a preocupação de Brian. O $ {myFolder} deve ser uma propriedade do sistema e não estar no arquivo de propriedades. Isso pode ser resolvido com a propriedade do sistema tomcat ou a propriedade run configurada no eclipse. Você pode até ter uma propriedade de construção. Esta solução supõe que um pouco e deve abordar isso, mas ao mesmo tempo essa resposta é muito mais parecida com a prática padrão de carregar propriedades de primavera e java em um local, em vez de separadamente. Outra opção é carregar um arquivo de propriedades gerais com o myFile no arquivo e usá-lo para obter o restante.
Rob
1
Tentei aplicar esta solução alternativa ao 'novo' PropertySourcesPlaceholderConfigurer do Spring 3.1+, mas descobri que o método processProperties (ConfigurableListableBeanFactory beanFactory, props Properties) agora está obsoleto e, portanto, agora não há acesso ao argumento 'props'. Examinar as fontes do PropertySourcesPlaceholderConfigurer não encontrou uma maneira limpa de expor as propriedades. Alguma idéia para fazer isso? Obrigado!
Jorge Palacio
48

Eu fiz isso e funcionou.

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

Isso deve funcionar.

Zoidberg
fonte
25

Você também pode usar os utilitários de mola ou carregar propriedades por meio do 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>

Em seguida, você pode buscá-los em seu aplicativo com:

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

e use adicionalmente estas propriedades em sua configuração:

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

Isso também está nos documentos: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties

enkor
fonte
10

Crie uma turma como abaixo

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

        }

    }

Onde quer que você queira acessar uma propriedade, use

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

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

- escreve getters e setters na classe acessadora

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

        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
user1419261
fonte
1
De longe, a melhor resposta, deve apenas autowire Environment.
sbochins
4

Como você sabe que as versões mais recentes do Spring não usam o PropertyPlaceholderConfigurer e agora usam outra construção de pesadelo chamada PropertySourcesPlaceholderConfigurer. Se você está tentando obter propriedades resolvidas do código e deseja que a equipe do Spring nos tenha fornecido uma maneira de fazer isso há muito tempo, vote nesta postagem! ... Porque é assim que você faz da nova maneira:

PropertySourcesPlaceholderConfigurer da subclasse:

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

Para usá-lo, use sua subclasse no seu @Configuration e salve uma referência a ele para uso posterior.

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

Uso:

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

Aqui está outra amostra.

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

Isso me ajuda a:

ApplicationContextUtils.getApplicationContext().getEnvironment()
Ruzal Yumaev
fonte
Que pacote é o ApplicationContextUtils dentro
Luke
2

Isso resolverá quaisquer propriedades aninhadas.

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

Você pode obter suas propriedades através da Environmentclasse. Como está a documentação:

As propriedades desempenham um papel importante em quase todos os aplicativos e podem se originar de uma variedade de fontes: arquivos de propriedades, propriedades do sistema JVM, variáveis ​​de ambiente do sistema, JNDI, parâmetros de contexto do servlet, objetos ad-hoc Properties, mapas e assim por diante. A função do objeto de ambiente em relação às propriedades é fornecer ao usuário uma interface de serviço conveniente para configurar fontes de propriedades e resolver propriedades a partir delas.

Tendo o ambiente como uma envvariável, basta chamar:

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

Você pode obter suas propriedades "brutas" através de:

env.getProperty("your-property")

Ele pesquisará todas as fontes de propriedades registradas pela primavera.

Você pode obter o Ambiente através de:

  • injete ApplicationContext implementando ApplicationContextAwaree depois chamegetEnvironment() contexto
  • implementar EnvironmentAware.

É obtido através da implementação de uma classe porque as propriedades são resolvidas no estágio inicial da inicialização do aplicativo, pois podem ser necessárias para a construção do bean.

Leia mais sobre a documentação: Spring Environment documentation

Augustin Ghauratto
fonte
1

Esta publicação também explica como acessar as propriedades: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

Você pode acessar as propriedades carregadas pelo spring property-placeholder sobre esse 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
fonte
0
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`

em servlet-context.xml depois você pode usar seu arquivo diretamente em qualquer lugar

Aniket
fonte
0

Use o código abaixo em seu arquivo de configuração do Spring para carregar o arquivo do caminho da classe do seu aplicativo

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

Esta é a melhor maneira de fazê-lo funcionar:

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
fonte
Instancie a classe e chame o método obj.getProperty ("my.property.name");
Daniel Almeida