स्प्रिंग: सभी पर्यावरणीय गुणों को मानचित्र या गुण ऑब्जेक्ट के रूप में एक्सेस करें


84

मैं अपने स्प्रिंग परिवेश को इस तरह कॉन्फ़िगर करने के लिए एनोटेशन का उपयोग कर रहा हूं:

@Configuration
...
@PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer 
{
    @Autowired
    Environment env;
}

यह मेरे गुणों का default.propertiesहिस्सा है Environment। मैं @PropertySourceयहां तंत्र का उपयोग करना चाहता हूं , क्योंकि यह पहले से ही पर्यावरण की सेटिंग्स (उदाहरण के लिए config_dir स्थान) के आधार पर कई फ़ॉलबैक परतों और विभिन्न गतिशील स्थानों के माध्यम से गुणों को अधिभारित करने की संभावना प्रदान करता है। मैंने उदाहरण को आसान बनाने के लिए केवल कमबैक छीन लिया।

हालाँकि, अब मेरी समस्या यह है कि मैं अपने डेटा स्रोत गुणों के उदाहरण के लिए कॉन्फ़िगर करना चाहता हूं default.properties। आप डेटासोर्स के लिए सेटिंग्स का उपयोग किए बिना विस्तार से जाने बिना सेटिंग्स को डेटा स्रोत पर भेज सकते हैं

Properties p = ...
datasource.setProperties(p);

हालाँकि, समस्या यह है कि Environmentवस्तु न तो कोई Propertiesवस्तु है और न ही कोई वस्तु है Map। देखने की मेरी बात से यह संभव नहीं पर्यावरण के सभी मूल्यों का उपयोग करने की, नहीं है क्योंकि वहाँ है keySetया iteratorविधि या कुछ भी तुलनीय।

Properties p <=== Environment env?

क्या मैं कुछ भूल रहा हूँ? क्या Environmentकिसी भी तरह से ऑब्जेक्ट की सभी प्रविष्टियों तक पहुंच संभव है ? यदि हाँ, मैं प्रविष्टियों को किसी ऑब्जेक्ट Mapया Propertiesऑब्जेक्ट पर मैप कर सकता हूं, तो मैं उन्हें फ़िल्टर भी कर सकता हूं या उन्हें उपसर्ग द्वारा मैप कर सकता हूं - एक मानक जावा के रूप में सबसेट बनाएं Map... यही वह है जो मैं करना चाहता हूं। कोई सुझाव?

जवाबों:


72

आपको कुछ इस तरह की आवश्यकता है, शायद इसे बेहतर बनाया जा सकता है। यह पहला प्रयास है:

...
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
...

@Configuration
...
@org.springframework.context.annotation.PropertySource("classpath:/config/default.properties")
...
public class GeneralApplicationConfiguration implements WebApplicationInitializer 
{
    @Autowired
    Environment env;

    public void someMethod() {
        ...
        Map<String, Object> map = new HashMap();
        for(Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator(); it.hasNext(); ) {
            PropertySource propertySource = (PropertySource) it.next();
            if (propertySource instanceof MapPropertySource) {
                map.putAll(((MapPropertySource) propertySource).getSource());
            }
        }
        ...
    }
...

मूल रूप से, पर्यावरण से सब कुछ जो एक है MapPropertySource(और काफी कार्यान्वयन हैं) Mapगुणों के रूप में पहुँचा जा सकता है ।


इस दृष्टिकोण को साझा करने के लिए धन्यवाद। मैं इसे थोड़ा "गंदा" मानता हूं, लेकिन यहां जाने का शायद एकमात्र तरीका है। एक अन्य सहयोगी ने मुझे दिखाया कि मुझे एक निश्चित कुंजी का उपयोग करके कॉन्फ़िगरेशन में एक संपत्ति डालनी होगी जो सभी संपत्ति कुंजी के साथ एक सूची रखती है। तब आप मुख्य सूची के आधार पर मानचित्र / गुण ऑब्जेक्ट में गुणों को पढ़ सकते हैं। कम से कम जातियों को रोका जाएगा ...
RoK

20
स्प्रिंग बूट के लिए ध्यान दें ... कि getPropertySources () प्रॉपर्टीस्रोस को पूर्ववर्ती क्रम में लौटाता है ताकि आपको प्रभावी रूप से रिवर्स करने की आवश्यकता है कि उन मामलों में जहां संपत्ति के मानों को ओवरराइट किया गया है
Rob Bygrave

2
जैसा कि @RobBygrave ने उल्लेख किया है कि आदेश अलग-अलग हो सकता है, लेकिन आदेश को वापस करने के बजाय (क्योंकि आप वसंत बूट को युद्ध के रूप में कंटेनर में तैनात कर सकते हैं या यह व्यवहार भविष्य में बदल सकता है) मैं बस सभी चाबियों को इकट्ठा करूंगा और फिर applicationContext.getEnvironment().getProperty(key)उन्हें हल करने के लिए उपयोग करूंगा
पोटा

@ आलू यह एक अच्छा विचार है, और मैंने कोशिश की है। एकमात्र संभावित समस्या यह है कि आप प्लेसहोल्डर्स के साथ मूल्यांकन के मुद्दों में भाग लेते हैं, जैसे कि इस प्रश्न में यहां: stackoverflow.com/questions/34584498/…
bischoje

1
धन्यवाद! .. मैं org.apache.ibatis.io.Resources.getResourceAsProperties ("फाइलपथ") के स्थान पर उपयोग करने के लिए एक स्प्रिंग विकल्प की तलाश कर रहा था। इस समाधान ने मेरे लिए बहुत अच्छा काम किया।
इतना यादृच्छिक-ड्यूड

67

यह एक पुराना प्रश्न है, लेकिन स्वीकृत उत्तर में एक गंभीर दोष है। यदि स्प्रिंग Environmentऑब्जेक्ट में कोई ओवरराइडिंग मान शामिल हैं (जैसा कि बाहरी कॉन्फ़िगरेशन में वर्णित है ), तो इस बात की कोई गारंटी नहीं है कि इसके द्वारा बनाए गए संपत्ति मानों का मानचित्र Environmentऑब्जेक्ट से लौटे हुए लोगों से मेल खाएगा । मैंने पाया है कि बस के माध्यम से पुनरावृत्ति PropertySourceकी रों Environmentनहीं, वास्तव में, किसी भी अधिभावी मान देना था। इसके बजाय इसने मूल मूल्य का उत्पादन किया, जिसे ओवरराइड किया जाना चाहिए था।

यहाँ एक बेहतर उपाय है। यह ज्ञात संपत्ति नामों के माध्यम से पुनरावृति EnumerablePropertySourceके Environmentलिए एस का उपयोग करता है , लेकिन फिर वास्तविक स्प्रिंग वातावरण से वास्तविक मूल्य को पढ़ता है। यह गारंटी देता है कि मूल्य वास्तव में स्प्रिंग द्वारा हल किया गया है, जिसमें किसी भी ओवरराइडिंग मान शामिल हैं।

Properties props = new Properties();
MutablePropertySources propSrcs = ((AbstractEnvironment) springEnv).getPropertySources();
StreamSupport.stream(propSrcs.spliterator(), false)
        .filter(ps -> ps instanceof EnumerablePropertySource)
        .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames())
        .flatMap(Arrays::<String>stream)
        .forEach(propName -> props.setProperty(propName, springEnv.getProperty(propName)));

1
यह ध्यान देने योग्य है कि स्प्रिंग 4.1.2 के रूप में, इस समाधान (अन्य उत्तरों के विपरीत) को समग्र रूप से CompitePropertySource से निपटने के लिए अद्यतन करने की आवश्यकता नहीं है, क्योंकि CompitePropertySource, EnumerablePropertySource का विस्तार करता है और इसलिए getPropertyNames समग्र में सभी संपत्ति नामों का सेट लौटाएगा। स्रोत।
एम। जस्टिन

5
आप collecta forEach: करने के बजाय स्ट्रीम पर अंतर्निहित पद्धति का उपयोग करके गुण एकत्र कर सकते हैं .distinct().collect(Collectors.toMap(Function.identity(), springEnv::getProperty))। यदि आपको इसे एक मानचित्र के बजाय एक गुण में इकट्ठा करने की आवश्यकता है, तो आप चार-तर्क संस्करण का उपयोग कर सकते हैं collect
एम। जस्टिन

2
क्या है springEnv? यह कहां से आता है? क्या यह envस्वीकृत समाधान से भिन्न है ?
२३:४३

2
@sebnukem अच्छा बिंदु। springEnvहै envमूल प्रश्न और स्वीकार किए जाते हैं समाधान की वस्तु। मुझे लगता है कि मुझे नाम वही रखना चाहिए था।
पेडोरो

3
आप उपयोग कर सकते हैं ConfigurableEnvironment और कलाकारों को नहीं करना है।
अभिजीत सरकार

19

मुझे उन सभी संपत्तियों को पुनः प्राप्त करने की आवश्यकता थी जिनकी कुंजी एक अलग उपसर्ग के साथ शुरू होती है (उदाहरण के लिए "log4j.appender।" से शुरू होने वाली सभी संपत्तियाँ) और निम्नलिखित कोड (जावा 8 के स्ट्रीम और लैमडा का उपयोग करके) लिखा।

public static Map<String,Object> getPropertiesStartingWith( ConfigurableEnvironment aEnv,
                                                            String aKeyPrefix )
{
    Map<String,Object> result = new HashMap<>();

    Map<String,Object> map = getAllProperties( aEnv );

    for (Entry<String, Object> entry : map.entrySet())
    {
        String key = entry.getKey();

        if ( key.startsWith( aKeyPrefix ) )
        {
            result.put( key, entry.getValue() );
        }
    }

    return result;
}

public static Map<String,Object> getAllProperties( ConfigurableEnvironment aEnv )
{
    Map<String,Object> result = new HashMap<>();
    aEnv.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) );
    return result;
}

public static Map<String,Object> getAllProperties( PropertySource<?> aPropSource )
{
    Map<String,Object> result = new HashMap<>();

    if ( aPropSource instanceof CompositePropertySource)
    {
        CompositePropertySource cps = (CompositePropertySource) aPropSource;
        cps.getPropertySources().forEach( ps -> addAll( result, getAllProperties( ps ) ) );
        return result;
    }

    if ( aPropSource instanceof EnumerablePropertySource<?> )
    {
        EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource;
        Arrays.asList( ps.getPropertyNames() ).forEach( key -> result.put( key, ps.getProperty( key ) ) );
        return result;
    }

    // note: Most descendants of PropertySource are EnumerablePropertySource. There are some
    // few others like JndiPropertySource or StubPropertySource
    myLog.debug( "Given PropertySource is instanceof " + aPropSource.getClass().getName()
                 + " and cannot be iterated" );

    return result;

}

private static void addAll( Map<String, Object> aBase, Map<String, Object> aToBeAdded )
{
    for (Entry<String, Object> entry : aToBeAdded.entrySet())
    {
        if ( aBase.containsKey( entry.getKey() ) )
        {
            continue;
        }

        aBase.put( entry.getKey(), entry.getValue() );
    }
}

ध्यान दें कि प्रारंभिक बिंदु विन्यास योग्य सूचना है जो एम्बेडेड संपत्ति को वापस करने में सक्षम है (विन्यास योग्य पर्यावरण पर्यावरण का प्रत्यक्ष वंशज है)। आप इसके द्वारा इसे स्‍वीकार कर सकते हैं:

@Autowired
private ConfigurableEnvironment  myEnv;

यदि आप बहुत विशेष प्रकार के संपत्ति स्रोतों का उपयोग नहीं करते हैं (जैसे कि JndiPropertySource, जो आमतौर पर स्प्रिंग ऑटोकॉन्फ़िगरेशन में उपयोग नहीं किया जाता है) तो आप पर्यावरण में आयोजित सभी गुणों को पुनः प्राप्त कर सकते हैं।

कार्यान्वयन पुनरावृत्ति क्रम पर निर्भर करता है जो वसंत स्वयं प्रदान करता है और पहले पाया संपत्ति लेता है, बाद में सभी समान नाम वाले गुणों को त्याग दिया जाता है। यह उसी व्यवहार को सुनिश्चित करना चाहिए जैसे कि पर्यावरण को सीधे एक संपत्ति के लिए कहा गया था (पहले पाया गया वापस लौटना)।

यह भी ध्यान दें कि लौटी हुई संपत्तियों का समाधान अभी तक नहीं हुआ है यदि उनमें $ {...} ऑपरेटर के साथ उपनाम शामिल हैं। यदि आप एक विशेष कुंजी को हल करना चाहते हैं, तो आपको सीधे पर्यावरण से पूछना होगा:

myEnv.getProperty( key );

1
क्यों न इस तरह से सभी कुंजियों की खोज की जाए और फिर उचित मूल्य संकल्प को लागू करने के लिए environment.getProperty का उपयोग किया जाए? यह सुनिश्चित करना चाहते हैं कि पर्यावरण ओवरराइड्स का सम्मान किया जाता है, उदाहरण के लिए application.dev.properties application.properties में डिफ़ॉल्ट मान को ओवरराइड करता है और जैसा कि आपने प्लेसहोल्डर eval का उल्लेख किया है।
GameSalutes 21

यही मैंने आखिरी पैराग्राफ में बताया। Env.getProperty का उपयोग करना वसंत के मूल व्यवहार को सुनिश्चित करता है।
हिरनी

आप इसे कैसे परखेंगे? NullPointerExceptionजब मुझे इसका @Autowiredउदाहरण मिलता है, तो मैं हमेशा अपने यूनिट परीक्षणों में शामिल होता हूं ConfigurationEnvironment
आर्टऑफवर्फ

क्या आप सुनिश्चित हैं कि आप अपना परीक्षण वसंत एप्लिकेशन के रूप में चला रहे हैं?
हेरी

मैं इसे इस तरह से करता हूँ:
हिरोई

10

मूल प्रश्न ने संकेत दिया कि उपसर्ग के आधार पर सभी गुणों को फ़िल्टर करने में सक्षम होना अच्छा होगा। मैंने अभी पुष्टि की है कि यह स्प्रिंग बूट 2.1.1 के रूप में काम करता है । कृपया, के लिए Properties या Map<String,String> । मुझे यकीन है कि यह अभी के लिए काम कर रहा है। दिलचस्प है, यह prefix =योग्यता के बिना काम नहीं करता है , यानी मुझे नहीं पता कि पूरे वातावरण को एक नक्शे में कैसे लोड किया जाए। जैसा कि मैंने कहा, यह वास्तव में वही हो सकता है जो ओपी के साथ शुरू करना चाहता था। उपसर्ग और निम्नलिखित '।' छीन लिया जाएगा, जो एक हो सकता है या नहीं हो सकता है:

@ConfigurationProperties(prefix = "abc")
@Bean
public Properties getAsProperties() {
    return new Properties();
}

@Bean
public MyService createService() {
    Properties properties = getAsProperties();
    return new MyService(properties);
}

पोस्टस्क्रिप्ट: पूरे वातावरण को प्राप्त करना वास्तव में संभव है, और शर्मनाक है। मैं नहीं जानता कि यह मुझे कैसे बच गया:

@ConfigurationProperties
@Bean
public Properties getProperties() {
    return new Properties();
}

1
इसके अलावा, एबीसी = एक्स जैसे गुण {बी = {सी = एक्स}} में
निहित हैं

इसमें कोई भी भाग काम नहीं करता है - getAsProperties()हमेशा एक खाली Propertiesउदाहरण देता है, और इसे बिना उपसर्ग के निर्दिष्ट करने की कोशिश करना भी इसे संकलित करने की अनुमति नहीं देता है। यह स्प्रिंग बूट 2.1.6 के साथ है। कृपया
आर्टऑफवर्फ

1
मैं काम पर जावा नहीं लिख रहा हूं, लेकिन मैंने इसे बहुत जल्दी मार दिया: github.com/AbuCarlo/SpringPropertiesBean । यह संभव है कि यह काम नहीं करेगा यदि आप किसी तरह स्प्रिंग के स्टार्टअप अनुक्रम (यानी "गुण" बीन को कभी भी आबाद नहीं करते हैं) को दरकिनार करते हैं। यह जावा 8, स्प्रिंग 2.2.6 के लिए है।
अबूनासर

5

इस वसंत के जीरा टिकट के रूप में , यह एक जानबूझकर डिजाइन है। लेकिन निम्न कोड मेरे लिए काम करता है।

public static Map<String, Object> getAllKnownProperties(Environment env) {
    Map<String, Object> rtn = new HashMap<>();
    if (env instanceof ConfigurableEnvironment) {
        for (PropertySource<?> propertySource : ((ConfigurableEnvironment) env).getPropertySources()) {
            if (propertySource instanceof EnumerablePropertySource) {
                for (String key : ((EnumerablePropertySource) propertySource).getPropertyNames()) {
                    rtn.put(key, propertySource.getProperty(key));
                }
            }
        }
    }
    return rtn;
}

2

स्प्रिंग को java.util.Propertiesस्प्रिंग एनवायरनमेंट से हटाने की अनुमति नहीं होगी ।

लेकिन Properties.load()अभी भी स्प्रिंग बूट एप्लिकेशन में काम करता है:

Properties p = new Properties();
try (InputStream is = getClass().getResourceAsStream("/my.properties")) {
    p.load(is);
}

1

अन्य जवाबों में शामिल अधिकांश मामलों के लिए समाधान PropertySourcesका उल्लेख किया गया है, लेकिन किसी ने भी उल्लेख नहीं किया है कि कुछ संपत्ति स्रोत उपयोगी प्रकारों में डाले जाने में असमर्थ हैं।

ऐसा ही एक उदाहरण कमांड लाइन के तर्कों के लिए संपत्ति स्रोत है। उपयोग किया जाने वाला वर्ग है SimpleCommandLinePropertySource। इस निजी वर्ग को एक सार्वजनिक विधि द्वारा लौटाया जाता है , इस प्रकार यह ऑब्जेक्ट के अंदर डेटा तक पहुंचने के लिए बेहद मुश्किल हो जाता है। मुझे डेटा पढ़ने और अंततः संपत्ति स्रोत को बदलने के लिए प्रतिबिंब का उपयोग करना पड़ा।

अगर किसी के पास बेहतर समाधान है, तो मैं वास्तव में इसे देखना चाहूंगा; हालाँकि, यह एकमात्र हैक है जिसे मैंने काम करने के लिए दिया है।


क्या आपने गैर-सार्वजनिक वर्ग के साथ समस्या का हल ढूंढ लिया है?
टोबियास

1

स्प्रिंग बूट 2 के साथ काम करना, मुझे कुछ ऐसा ही करने की जरूरत थी। ऊपर दिए गए अधिकतर उत्तर ठीक काम करते हैं, बस इस बात से सावधान रहें कि ऐप के विभिन्न चरणों में जीवन चक्र अलग-अलग होंगे।

उदाहरण के लिए, ApplicationEnvironmentPreparedEventकिसी भी गुण के अंदर application.propertiesमौजूद नहीं होने के बाद । हालांकि, एक ApplicationPreparedEventघटना के बाद वे हैं।


1

स्प्रिंग बूट के लिए, स्वीकृत उत्तर डुप्लिकेट गुणों को ओवरराइट कर देगा कम प्राथमिकता वाले । यह समाधान गुणों को एक में एकत्रित करेगा SortedMapऔर केवल सर्वोच्च प्राथमिकता वाले डुप्लिकेट गुणों को ले जाएगा।

final SortedMap<String, String> sortedMap = new TreeMap<>();
for (final PropertySource<?> propertySource : env.getPropertySources()) {
    if (!(propertySource instanceof EnumerablePropertySource))
        continue;
    for (final String name : ((EnumerablePropertySource<?>) propertySource).getPropertyNames())
        sortedMap.computeIfAbsent(name, propertySource::getProperty);
}

env.getPropertySources () सबसे कम प्राथमिकता में गुण देता है?
फराज

यह दूसरा रास्ता है। वे उच्च -> निम्न प्राथमिकता से हल किए जाते हैं।
सैमुअल तातिपमुला

0

हालांकि मैं एक और रास्ता जोड़ूंगा। मेरे मामले में मैं इसे आपूर्ति करता हूं com.hazelcast.config.XmlConfigBuilderजिसमें केवल java.util.Propertiesहेज़लकास्ट एक्सएमएल कॉन्फ़िगरेशन फ़ाइल के अंदर कुछ गुणों को हल करने की आवश्यकता होती है, अर्थात यह केवल कॉल getProperty(String)विधि है। इसलिए, इससे मुझे वह करने की अनुमति मिली, जिसकी मुझे आवश्यकता थी:

@RequiredArgsConstructor
public class SpringReadOnlyProperties extends Properties {

  private final org.springframework.core.env.Environment delegate;

  @Override
  public String getProperty(String key) {
    return delegate.getProperty(key);
  }

  @Override
  public String getProperty(String key, String defaultValue) {
    return delegate.getProperty(key, defaultValue);
  }

  @Override
  public synchronized String toString() {
    return getClass().getName() + "{" + delegate + "}";
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    if (!super.equals(o)) return false;
    SpringReadOnlyProperties that = (SpringReadOnlyProperties) o;
    return delegate.equals(that.delegate);
  }

  @Override
  public int hashCode() {
    return Objects.hash(super.hashCode(), delegate);
  }

  private void throwException() {
    throw new RuntimeException("This method is not supported");
  }

  //all methods below throw the exception

  * override all methods *
}

PS मैंने इसे विशेष रूप से हेज़ेलकास्ट के लिए उपयोग नहीं किया, क्योंकि यह केवल XML फ़ाइल के लिए गुणों को हल करता है, लेकिन रनटाइम पर नहीं। चूंकि मैं भी स्प्रिंग का उपयोग करता हूं, इसलिए मैंने एक रिवाज के साथ जाने का फैसला किया org.springframework.cache.interceptor.AbstractCacheResolver#getCacheNames। यह दोनों स्थितियों के लिए गुणों को हल करता है, कम से कम यदि आप कैश नामों में गुणों का उपयोग करते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.