स्प्रिंग बूट - application.yml से मानचित्र इंजेक्ट करें


99

मेरे पास निम्नलिखित के साथ एक स्प्रिंग बूट एप्लिकेशन है application.yml- मूल रूप से यहां से लिया गया है :

info:
   build:
      artifact: ${project.artifactId}
      name: ${project.name}
      description: ${project.description}
      version: ${project.version}

मैं विशेष मूल्यों को इंजेक्ट कर सकता हूं, जैसे

@Value("${info.build.artifact}") String value

हालाँकि, मैं पूरे मानचित्र को इंजेक्ट करना चाहूँगा, अर्थात ऐसा कुछ:

@Value("${info}") Map<String, Object> info

क्या यह (या कुछ इसी तरह) संभव है? जाहिर है, मैं सीधे yaml को लोड कर सकता हूं, लेकिन सोच रहा था कि क्या स्प्रिंग द्वारा पहले से ही समर्थित कुछ है।

जवाबों:


71

आप का उपयोग कर एक नक्शा हो सकता है @ConfigurationProperties:

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties
public class MapBindingSample {

    public static void main(String[] args) throws Exception {
        System.out.println(SpringApplication.run(MapBindingSample.class, args)
                .getBean(Test.class).getInfo());
    }

    @Bean
    @ConfigurationProperties
    public Test test() {
        return new Test();
    }

    public static class Test {

        private Map<String, Object> info = new HashMap<String, Object>();

        public Map<String, Object> getInfo() {
            return this.info;
        }
    }
}

इस सवाल में yaml के साथ चल रहा है:

{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}}

प्रीफ़िक्स सेट करने के लिए विभिन्न विकल्प हैं, यह नियंत्रित करना कि लापता गुण कैसे संभाले जाते हैं, आदि अधिक जानकारी के लिए javadoc देखें ।


धन्यवाद एंडी - यह उम्मीद के मुताबिक काम करता है। दिलचस्प है कि यह एक अतिरिक्त वर्ग के बिना काम नहीं करता है - यानी आप किसी कारण से infoनक्शा नहीं डाल सकते हैं MapBindingSample(हो सकता है कि इसका उपयोग SpringApplication.runकॉल में ऐप को चलाने के लिए किया जा रहा हो )।
लेवांत

1
क्या उप-मानचित्र को इंजेक्ट करने का एक तरीका है? उदाहरण के लिए उपरोक्त मानचित्र के info.buildबजाय इंजेक्ट infoकरें?
लेवेंट

1
हाँ। जानकारी के लिए @ConfigurationProperties पर उपसर्ग सेट करें और फिर GetBuild ()
एंडी विल्किंसन

अच्छा, धन्यवाद एंडी, एक आकर्षण की तरह काम किया! एक और बात - जब सेटिंग locations( ymlडिफ़ॉल्ट के बजाय किसी अन्य फ़ाइल से गुण प्राप्त करने के लिए application.yml) पर @ConfigurationProperties, यह काम किया, सिवाय इसके कि प्लेसहोल्डर्स को प्रतिस्थापित नहीं किया गया। उदाहरण के लिए, यदि आपके पास एक सिस्टम प्रॉपर्टी project.version=123सेट है, तो आपने उत्तर में जो उदाहरण दिया है, वह सेट version=123करते समय locationsवापस आ जाएगा project.version=${project.version}। क्या आपको पता है कि यहाँ किसी प्रकार की सीमा है?
लेवांत

वह एक सीमा है। जब आपने किसी कस्टम स्थान का उपयोग किया हो तो प्लेसहोल्डर प्रतिस्थापन करने के लिए मैंने एक मुद्दा ( github.com/spring-projects/spring-boot/issues/1301 ) खोल दिया है
एंडी विल्किंसन

109

नीचे समाधान @Andy Wilkinson के समाधान के लिए एक आशुलिपि है, सिवाय इसके कि इसे एक अलग वर्ग या @Beanएनोटेट विधि पर उपयोग करने की आवश्यकता नहीं है ।

application.yml:

input:
  name: raja
  age: 12
  somedata:
    abcd: 1 
    bcbd: 2
    cdbd: 3

SomeComponent.java:

@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "input")
class SomeComponent {

    @Value("${input.name}")
    private String name;

    @Value("${input.age}")
    private Integer age;

    private HashMap<String, Integer> somedata;

    public HashMap<String, Integer> getSomedata() {
        return somedata;
    }

    public void setSomedata(HashMap<String, Integer> somedata) {
        this.somedata = somedata;
    }

}

हम @Valueएनोटेशन और @ConfigurationProperties, दोनों मुद्दों को क्लब कर सकते हैं। लेकिन गेटर्स और सेटर महत्वपूर्ण हैं और काम करने @EnableConfigurationPropertiesके @ConfigurationPropertiesलिए जरूरी है ।

मैंने इस विचार को @Szymon Stepniak द्वारा प्रदान किए गए groovy समाधान से आज़माया, सोचा कि यह किसी के लिए उपयोगी होगा।


11
धन्यवाद! मैंने स्प्रिंग बूट 1.3.1 का उपयोग किया, मेरे मामले में मैंने पाया कि जरूरत नहीं है@EnableConfigurationProperties
zhuguowei

इस उत्तर का उपयोग करते समय मुझे 'अमान्य वर्ण स्थिरांक' त्रुटि मिलती है। क्या आप इस त्रुटि को रोकने के लिए दोहरे उद्धरण चिह्नों का उपयोग करने के लिए @ConfigurationProperties (उपसर्ग = 'इनपुट') बदल सकते हैं।
एंटोन रैंड

10
अच्छा जवाब, लेकिन @Value एनोटेशन आवश्यक नहीं हैं।
रॉबिन

3
डमी गेट्टर और सेटर लिखने के बजाय आप लोम्बोक एनोटेशन @ सिटर (एक्सेसलेव.पब्लिक) और @ गेट्टर (एक्सेसलीव.पब्लिक) का उपयोग कर सकते हैं
रिज्किट

जिनियस। ध्यान दें कि कॉन्फ़िगरेशन को नेस्टेड भी किया जा सकता है: मैप <स्ट्रींग, मैप <स्ट्रिंग, स्ट्रिंग >>
मथे एंड्रे-बोंड

16

मैं आज भी इसी समस्या में हूं, लेकिन दुर्भाग्य से एंडी का समाधान मेरे लिए कारगर नहीं रहा। स्प्रिंग बूट 1.2.1 में। यह और भी आसान है, लेकिन आपको कुछ चीजों के बारे में पता होना चाहिए।

यहाँ मेरे से दिलचस्प हिस्सा है application.yml:

oauth:
  providers:
    google:
     api: org.scribe.builder.api.Google2Api
     key: api_key
     secret: api_secret
     callback: http://callback.your.host/oauth/google

providersमानचित्र में केवल एक मानचित्र प्रविष्टि है, मेरा लक्ष्य अन्य OAuth प्रदाताओं के लिए गतिशील कॉन्फ़िगरेशन प्रदान करना है। मैं इस नक्शे को एक सेवा में इंजेक्ट करना चाहता हूं जो इस yaml फ़ाइल में प्रदान किए गए कॉन्फ़िगरेशन के आधार पर सेवाओं को आरंभ करेगा। मेरा प्रारंभिक कार्यान्वयन था:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    private Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

एप्लिकेशन शुरू करने के बाद, providersमैप को OAuth2ProvidersServiceइनिशियलाइज़ नहीं किया गया था। मैंने एंडी द्वारा सुझाए गए समाधान की कोशिश की, लेकिन यह भी काम नहीं किया। मैं उस एप्लिकेशन में ग्रूवी का उपयोग करता हूं , इसलिए मैंने हटाने का फैसला किया privateऔर ग्रूवी को गेट्टर और सेटर उत्पन्न करने दिया। तो मेरा कोड इस तरह देखा:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

उस छोटे से बदलाव के बाद सब कुछ काम कर गया।

हालांकि एक बात है जो ध्यान देने योग्य हो सकती है। काम करने के बाद मैंने इस क्षेत्र को बनाने privateऔर सेटर विधि में सीधे तर्क प्रकार के साथ सेटर प्रदान करने का निर्णय लिया । दुर्भाग्य से यह काम नहीं करेगा। यह org.springframework.beans.NotWritablePropertyExceptionसंदेश के साथ कारण बनता है :

Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Cannot access indexed value in property referenced in indexed property path 'providers[google]'; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Bean property 'providers[google]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

यदि आप अपने स्प्रिंग बूट एप्लिकेशन में ग्रूवी का उपयोग कर रहे हैं तो इसे ध्यान में रखें।


15

कॉन्फ़िगरेशन से मानचित्र को पुनः प्राप्त करने के लिए आपको कॉन्फ़िगरेशन वर्ग की आवश्यकता होगी। @Value एनोटेशन दुर्भाग्य से, चाल नहीं चलेगा।

Application.yml

entries:
  map:
     key1: value1
     key2: value2

विन्यास वर्ग:

@Configuration
@ConfigurationProperties("entries")
@Getter
@Setter
 public static class MyConfig {
     private Map<String, String> map;
 }

संस्करण 2.1.0 के खिलाफ उपरोक्त समाधान कार्यों का परीक्षण किया गया
तुगरुल आसन

6

अनुप्रयोग से @Value का उपयोग कर मानचित्र खींचने के लिए समाधान। बहु संपत्ति के रूप में कोडित संपत्ति

application.yml

other-prop: just for demo 

my-map-property-name: "{\
         key1: \"ANY String Value here\", \  
         key2: \"any number of items\" , \ 
         key3: \"Note the Last item does not have comma\" \
         }"

other-prop2: just for demo 2 

यहाँ हमारी मैप प्रॉपर्टी "माय-मैप-प्रॉपर्टी-नेम" का मान स्ट्रिंग के अंदर JSON फॉर्मेट में स्टोर किया गया है और हमने लाइन के अंत में \ _ "का उपयोग करके मल्टीलाइन प्राप्त किया है।

myJavaClass.java

import org.springframework.beans.factory.annotation.Value;

public class myJavaClass {

@Value("#{${my-map-property-name}}") 
private Map<String,String> myMap;

public void someRandomMethod (){
    if(myMap.containsKey("key1")) {
            //todo...
    } }

}

अधिक स्पष्टीकरण

  • \ _ yaml में इसका उपयोग मल्टीलाइन में स्ट्रिंग को तोड़ने के लिए किया जाता है

  • yaml string में "(quot) के लिए \" एस्केप चार्टर है

  • {key: value} JSON in yaml जिसे @Value द्वारा मैप में परिवर्तित किया जाएगा

  • {{} यह स्पेल एक्सप्रेशन है और इसका उपयोग @Value में जोंस इंट मैप या एरे / लिस्ट संदर्भ बदलने के लिए किया जा सकता है

स्प्रिंग बूट प्रोजेक्ट में परीक्षण किया गया


3
foo.bars.one.counter=1
foo.bars.one.active=false
foo.bars[two].id=IdOfBarWithKeyTwo

public class Foo {

  private Map<String, Bar> bars = new HashMap<>();

  public Map<String, Bar> getBars() { .... }
}

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding


7
ढेर अतिप्रवाह में आपका स्वागत है! हालांकि यह कोड स्निपेट प्रश्न को हल कर सकता है, जिसमें स्पष्टीकरण सहित वास्तव में आपकी पोस्ट की गुणवत्ता में सुधार करने में मदद करता है। याद रखें कि आप भविष्य में पाठकों के लिए प्रश्न का उत्तर दे रहे हैं, और उन लोगों को आपके कोड सुझाव के कारणों का पता नहीं चल सकता है।
स्कॉट वेल्डन

हालांकि विकि के लिए लिंक मूल्यवान है। स्पष्टीकरण github.com/spring-projects/spring-boot/wiki/… पर है
dschulten

1

यदि आप अतिरिक्त संरचनाओं से बचना चाहते हैं, तो आप इसे और भी सरल बना सकते हैं।

service:
  mappings:
    key1: value1
    key2: value2
@Configuration
@EnableConfigurationProperties
public class ServiceConfigurationProperties {

  @Bean
  @ConfigurationProperties(prefix = "service.mappings")
  public Map<String, String> serviceMappings() {
    return new HashMap<>();
  }

}

और फिर इसे हमेशा की तरह उपयोग करें, उदाहरण के लिए एक निर्माता के साथ:

public class Foo {

  private final Map<String, String> serviceMappings;

  public Foo(Map<String, String> serviceMappings) {
    this.serviceMappings = serviceMappings;
  }

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