Akses file properti secara terprogram dengan Spring?


139

Kami menggunakan kode di bawah ini untuk menyuntikkan kacang musim semi dengan properti dari file properti.

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

Apakah ada cara agar kita dapat mengakses properti secara terprogram? Saya mencoba melakukan beberapa kode tanpa injeksi ketergantungan. Jadi saya hanya ingin memiliki beberapa kode seperti ini:

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

Contoh lengkap mengakses file properti di musim semi ada di tautan berikut: bharatonjava.wordpress.com/2012/08/24/…

Jawaban:


175

Bagaimana dengan PropertiesLoaderUtils ?

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

6
ini adalah pertanyaan, apa bedanya dengan saya, dan memiliki dua suara lagi DAN diposting kedua ...
Zoidberg

3
Pukul saya, saya tidak bisa memilih :) Saya tidak akan menggunakan PropertyPlaceholderConfigurer, meskipun itu berlebihan untuk tugas tersebut.
Skaffman

5
Saya berusaha sedekat mungkin dengan apa yang dia miliki, saya telah berkali-kali diremehkan karena tidak memberikan cukup detail. Bagaimanapun, jawaban Anda layak mendapatkan suara, karena itu benar, saya kira saya hanya cemburu, saya juga tidak mendapatkan 2 suara, LOL.
Zoidberg

2
Apa yang harus kita berikan di path jika file ditempatkan di direktori eksternal, misalkan folder config?
prnjn

55

Jika yang ingin Anda lakukan hanyalah mengakses nilai placeholder dari kode, ada @Valuepenjelasannya:

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

Untuk mengakses placeholder Dari SPEL gunakan sintaks ini:

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

Untuk mengekspos konfigurasi ke tampilan yang SPEL-nya dinonaktifkan, seseorang dapat menggunakan trik ini:

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

Dan kemudian gunakan exposer untuk mengekspos properti ke tampilan:

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

Kemudian dalam tampilan, gunakan properti yang terbuka seperti ini:

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

Solusi ini memiliki keuntungan karena Anda dapat mengandalkan penerapan placeholder standar yang dimasukkan oleh konteks: tag placeholder properti.

Sekarang sebagai catatan terakhir, jika Anda benar-benar membutuhkan a untuk menangkap semua properti placeholder dan nilainya, Anda harus menyalurkannya melalui StringValueResolver untuk memastikan bahwa placeholder berfungsi di dalam nilai properti seperti yang diharapkan. Kode berikut akan melakukannya.

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

Terima kasih untuk jawaban yang sangat lengkap ini! apakah ada cara untuk melakukan ini dengan bidang akhir?
Lingkungan

2
@ WardC Anda tidak bisa menyuntikkan ke bidang akhir. Namun, Anda dapat memasukkan ke argumen konstruktor dan menetapkan nilai bidang akhir di dalam konstruktor. Lihat stackoverflow.com/questions/2306078/… dan stackoverflow.com/questions/4203302/…
anttix

50

KREDIT : Akses terprogram ke properti di Spring tanpa membaca ulang file properti

Saya telah menemukan implementasi yang bagus untuk mengakses properti secara terprogram di musim semi tanpa memuat ulang properti yang sama yang telah dimuat oleh spring. [Selain itu, tidak perlu melakukan hardcode lokasi file properti di sumber]

Dengan perubahan ini, kode terlihat lebih bersih & lebih mudah dirawat.

Konsepnya cukup sederhana. Perluas placeholder properti default pegas (PropertyPlaceholderConfigurer) dan tangkap properti yang dimuatnya di variabel lokal

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

}

Contoh Penggunaan

SpringPropertiesUtil.getProperty("myProperty")

Perubahan konfigurasi pegas

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

Semoga ini bisa membantu memecahkan masalah yang Anda miliki


8
Ini bukan implementasi penuh dan tidak akan bekerja dengan benar. PropertyPlaceholderConfigurer menggunakan PropertyPlaceholderHelper untuk menggantikan SEMUA properti placeholder, termasuk placeholder bersarang. Dalam implementasi Kalinga jika Anda memiliki sesuatu seperti myFile = $ {myFolder} /myFile.txt, nilai properti literal yang akan Anda dapatkan dari peta menggunakan kunci "myFile" adalah $ {myFolder} /myFile.txt.

1
Ini adalah solusi yang tepat. Untuk mengatasi kekhawatiran Brian. $ {MyFolder} harus merupakan properti sistem dan tidak ada dalam file properti. Ini dapat diselesaikan dengan mengatur properti sistem kucing jantan atau menjalankan properti di gerhana. Anda bahkan mungkin bisa memiliki properti build. Solusi ini mengasumsikan bahwa sedikit dan harus mengatasinya tetapi pada saat yang sama jawaban ini jauh lebih sejalan dengan praktik standar untuk memuat properti pegas dan java di satu tempat daripada secara terpisah. Pilihan lainnya adalah memuat file properti umum dengan myFile di file tersebut dan menggunakannya untuk mendapatkan sisanya.
Rob

2
Saya mencoba menerapkan solusi ini ke PropertySourcesPlaceholderConfigurer 'baru' dari Spring 3.1+ tetapi saya telah menemukan bahwa metode processProperties (ConfigurableListableBeanFactory beanFactory, properti props) sekarang sudah usang dan karena itu sekarang tidak ada akses ke argumen 'props'. Melihat sumber PropertySourcesPlaceholderConfigurer tidak dapat menemukan cara yang bersih untuk mengekspos properti. Ada ide untuk melakukannya? Terima kasih!
Jorge Palacio

48

Saya telah melakukan ini dan berhasil.

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

Seharusnya itu berhasil.


26

Anda juga dapat menggunakan utilitas pegas, atau memuat properti melalui PropertiesFactoryBean.

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

atau:

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

Kemudian Anda dapat mengambilnya di aplikasi Anda dengan:

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

dan sebagai tambahan gunakan properti ini di konfigurasi Anda:

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

Ini juga ada di dokumen: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties


10

Buat kelas seperti di bawah ini

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

        }

    }

Kemudian di mana pun Anda ingin mengakses, gunakan properti

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

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

- tulis pengambil dan penyetel di kelas pengakses

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

        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }

1
Sejauh ini jawaban terbaik, seharusnya hanya Autowire Environment.
sbochins

4

Seperti yang Anda ketahui, versi Spring yang lebih baru tidak menggunakan PropertyPlaceholderConfigurer dan sekarang menggunakan konstruksi mimpi buruk lain yang disebut PropertySourcesPlaceholderConfigurer. Jika Anda mencoba menyelesaikan properti dari kode, dan berharap tim Spring memberi kami cara untuk melakukan ini sejak lama, maka berikan suara untuk postingan ini! ... Karena ini adalah cara Anda melakukannya dengan cara baru:

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

Untuk menggunakannya, pastikan untuk menggunakan subclass Anda di @Configuration Anda dan simpan referensi untuk itu untuk digunakan nanti.

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

Pemakaian:

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


3

Anda bisa mendapatkan properti Anda melalui Environmentkelas. Sebagai dokumentasi:

Properti memainkan peran penting di hampir semua aplikasi, dan dapat berasal dari berbagai sumber: file properti, properti sistem JVM, variabel lingkungan sistem, JNDI, parameter konteks servlet, objek Properti ad-hoc, Peta, dan sebagainya. Peran objek lingkungan terkait dengan properti adalah untuk menyediakan antarmuka layanan yang nyaman bagi pengguna untuk mengonfigurasi sumber properti dan menyelesaikan properti darinya.

Memiliki Lingkungan sebagai envvariabel, cukup panggil:

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

Anda bisa mendapatkan properti 'mentah' Anda melalui:

env.getProperty("your-property")

Ini akan mencari melalui semua sumber properti yang musim semi telah terdaftar.

Anda bisa mendapatkan Lingkungan melalui:

  • menyuntikkan ApplicationContext dengan mengimplementasikan ApplicationContextAwaredan kemudian memanggil getEnvironment()konteks
  • menerapkan EnvironmentAware.

Ini diperoleh melalui implementasi kelas karena properti diselesaikan pada tahap awal aplikasi startup, karena mereka mungkin diperlukan untuk konstruksi kacang.

Baca lebih lanjut tentang dokumentasi: dokumentasi Lingkungan musim semi


2

Ini contoh lainnya.

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

2

Ini akan menyelesaikan semua properti bertingkat.

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

1

Posting ini juga menjelaskan cara mengakses properti: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

Anda dapat mengakses properti yang dimuat oleh placeholder properti musim semi di atas kacang musim semi tersebut:

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

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

di servlet-context.xml setelah itu Anda bisa langsung menggunakan file Anda di mana saja


0

Gunakan kode di bawah ini di file konfigurasi pegas Anda untuk memuat file dari jalur kelas aplikasi Anda

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

0

Ini adalah cara terbaik saya membuatnya bekerja:

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

Buat instance kelas dan panggil metode obj.getProperty ("my.property.name");
Daniel Almeida
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.