¿Acceso al archivo de propiedades mediante programación con Spring?

137

Usamos el siguiente código para inyectar Spring beans con propiedades de un archivo de propiedades.

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

¿Hay alguna forma de acceder a las propiedades mediante programación? Estoy tratando de hacer algo de código sin inyección de dependencia. Entonces me gustaría tener un código como este:

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
Marcus Leon
fuente
Un ejemplo completo de acceso al archivo de propiedades en primavera está en el siguiente enlace: bharatonjava.wordpress.com/2012/08/24/…

Respuestas:

171

¿Qué hay de PropertiesLoaderUtils ?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
skaffman
fuente
55
He aquí una pregunta, ¿cómo es esto diferente de la mía, y tiene dos más votos y publicado segundos ...
Zoidberg
3
Me gana, no pude votar :) No usaría un PropertyPlaceholderConfigurer, aunque es excesivo para la tarea.
skaffman el
55
Intentaba acercarme lo más posible a lo que tenía, me han rechazado tantas veces por no proporcionar suficientes detalles. En cualquier caso, sus respuestas merecen los votos, ya que es correcto, creo que estoy celoso de que no obtuve 2 votos también, LOL.
Zoidberg
1
¿Qué debemos dar en ruta si el archivo se coloca en un directorio externo, digamos carpeta de configuración?
prnjn
52

Si todo lo que desea hacer es acceder al valor del marcador de posición desde el código, existe la @Valueanotación:

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

Para acceder a marcadores de posición desde SPEL, use esta sintaxis:

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

Para exponer la configuración a vistas que tienen SPEL desactivado, uno puede usar este truco:

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

Y luego use el expositor para exponer propiedades a una vista:

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

Luego, a la vista, use las propiedades expuestas de esta manera:

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

Esta solución tiene la ventaja de que puede confiar en la implementación de marcador de posición estándar inyectada por el contexto: etiqueta de marcador de posición de propiedad.

Ahora como nota final, si realmente necesita un para capturar todas las propiedades de marcador de posición y sus valores, debe canalizarlos a través de StringValueResolver para asegurarse de que los marcadores de posición funcionen dentro de los valores de propiedad como se esperaba. El siguiente código hará eso.

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(); }
}
antixix
fuente
Gracias por esta respuesta muy completa! ¿Hay alguna manera de hacer esto con los campos finales?
Ward,
2
@WardC no puede inyectarse en un campo final. Sin embargo, puede inyectar en un argumento de constructor y establecer un valor de campo final dentro del constructor. Ver stackoverflow.com/questions/2306078/… y stackoverflow.com/questions/4203302/…
anttix
50

CRÉDITO : acceso programático a las propiedades en Spring sin volver a leer el archivo de propiedades

He encontrado una buena implementación de acceder a las propiedades mediante programación en primavera sin volver a cargar las mismas propiedades que Spring ya ha cargado. [Además, no es necesario codificar la ubicación del archivo de propiedades en la fuente]

Con estos cambios, el código se ve más limpio y más fácil de mantener.

El concepto es muy simple. Simplemente extienda el marcador de posición de propiedad predeterminado de primavera (PropertyPlaceholderConfigurer) y capture las propiedades que carga en la variable 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();
    }

}

Ejemplo de uso

SpringPropertiesUtil.getProperty("myProperty")

Cambios de configuración de primavera

<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 esto ayude a resolver los problemas que tienes

Ashok Koyi
fuente
8
Esta no es una implementación completa y no funcionará correctamente. PropertyPlaceholderConfigurer utiliza un PropertyPlaceholderHelper para reemplazar TODAS las propiedades de marcador de posición, incluidos los marcadores de posición anidados. En la implementación de Kalinga si tiene algo como myFile = $ {myFolder} /myFile.txt, el valor de propiedad literal que obtendrá del mapa con la clave "myFile" será $ {myFolder} /myFile.txt.
1
Esta es la solución correcta. Para abordar la preocupación de Brian. $ {MyFolder} debe ser una propiedad del sistema y no estar en el archivo de propiedades. Esto se puede resolver al tener la propiedad del sistema tomcat o ejecutar la propiedad establecida en eclipse. Incluso podría tener una propiedad de construcción. Esta solución supone un poco y debería abordar eso, pero al mismo tiempo esta respuesta es mucho más similar a la práctica estándar para cargar las propiedades de primavera y Java en un lugar en lugar de por separado. Otra opción es cargar un archivo de propiedades generales con myFile en el archivo y usarlo para obtener el resto.
Rob
1
Intenté aplicar esta solución alternativa al 'nuevo' PropertySourcesPlaceholderConfigurer de Spring 3.1+ pero descubrí que el método processProperties (ConfigurableListableBeanFactory beanFactory, Properties props) ahora está en desuso y, por lo tanto, ahora no hay acceso al argumento 'props'. Mirando las fuentes de PropertySourcesPlaceholderConfigurer no se puede encontrar una manera limpia de exponer las propiedades. ¿Alguna idea para hacerlo? ¡Gracias!
Jorge Palacio
48

He hecho esto y ha funcionado.

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

Eso debería funcionar.

Zoidberg
fuente
25

También puede usar las utilidades spring o cargar propiedades a través de PropertiesFactoryBean.

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

o:

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

Luego puede recogerlos en su aplicación con:

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

y además usa estas propiedades en tu configuración:

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

Esto también está en los documentos: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties

enkor
fuente
10

Crea una clase como la siguiente

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

        }

    }

Luego, donde quiera acceder a un uso de propiedad

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

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

- escribir getters y setters en la clase de acceso

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

        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
usuario1419261
fuente
1
Con mucho, la mejor respuesta, solo debe conectar automáticamente el entorno.
sbochins
4

Como saben, las versiones más nuevas de Spring no usan PropertyPlaceholderConfigurer y ahora usan otra construcción de pesadilla llamada PropertySourcesPlaceholderConfigurer. Si está tratando de obtener propiedades resueltas del código, y desea que el equipo de Spring nos haya dado una forma de hacerlo hace mucho tiempo, ¡vote esta publicación! ... Porque así es como lo haces de la nueva manera:

Subclase PropertySourcesPlaceholderConfigurer:

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 usarlo, asegúrese de usar su subclase en su @Configuration y guarde una referencia para su 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
fuente
2

Aquí hay otra muestra.

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

Esto me ayuda:

ApplicationContextUtils.getApplicationContext().getEnvironment()
Ruzal Yumaev
fuente
¿En qué paquete está ApplicationContextUtils?
Luke
2

Esto resolverá cualquier propiedad anidada.

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

Puede obtener sus propiedades a través de la Environmentclase. Tal como está la documentación:

Las propiedades juegan un papel importante en casi todas las aplicaciones, y pueden originarse en una variedad de fuentes: archivos de propiedades, propiedades del sistema JVM, variables de entorno del sistema, JNDI, parámetros de contexto de servlet, objetos de propiedades ad-hoc, mapas, etc. La función del objeto de entorno con relación a las propiedades es proporcionar al usuario una interfaz de servicio conveniente para configurar fuentes de propiedades y resolver propiedades de ellas.

Teniendo el entorno como una envvariable, simplemente llame:

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

Puede obtener sus propiedades 'en bruto' a través de:

env.getProperty("your-property")

Buscará a través de todas las propiedades que la primavera haya registrado.

Puede obtener Medio Ambiente a través de:

  • inyectar ApplicationContext implementando ApplicationContextAwarey luego llamar getEnvironment()al contexto
  • poner en práctica EnvironmentAware.

Se obtiene mediante la implementación de una clase porque las propiedades se resuelven en la etapa inicial del inicio de la aplicación, ya que pueden ser necesarias para la construcción de beans.

Leer más sobre documentación: documentación de Spring Environment

Augustin Ghauratto
fuente
1

Esta publicación también explica cómo acceder a las propiedades: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

Puede acceder a las propiedades cargadas por el marcador de posición de propiedad spring sobre dicho bean spring:

@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
fuente
0
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`

en servlet-context.xml después de eso, puede usar directamente su archivo en todas partes

Aniket
fuente
0

Utilice el siguiente código en su archivo de configuración de primavera para cargar el archivo desde la ruta de clase de su aplicación

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

Esta es la mejor forma en que lo hice 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
fuente
Instanciar la clase y el método de llamada obj.getProperty ("my.property.name");
Daniel Almeida