2011-10-14 22 views
11

मेरे पास बैकएंड सेवा के डिफ़ॉल्ट निहितार्थ के साथ चल रहा वेब एप्लिकेशन है। एक इंटरफेस को कार्यान्वित करने और जार को प्लगइन्स फ़ोल्डर में छोड़ने में सक्षम होना चाहिए (जो ऐप्स क्लासपाथ में नहीं है)। एक बार सर्वर को पुनरारंभ करने के बाद, विचार है कि नया जार क्लासलोडर में लोड करना है, और यह निर्भरता इंजेक्शन में हिस्सा लेता है। मैं @Autowired का उपयोग कर स्प्रिंग डी का उपयोग कर रहा हूँ। नई प्लगइन सेवा प्रत्यारोपण @Primary एनोटेशन होगा। तो इंटरफेस के दो इम्प्ले दिए गए, प्राथमिक लोड किया जाना चाहिए।वसंत निर्भरता इंजेक्शन और प्लगइन जार

मुझे जार को क्लासलोडर में लोड किया गया है और मैन्युअल रूप से इम्प्लायर का आह्वान कर सकता है। लेकिन मैं निर्भरता इंजेक्शन में भाग लेने में सक्षम नहीं हूं, और इसे डिफ़ॉल्ट प्रत्यारोपण में बदल दिया है।

यहाँ एक सरल उदाहरण है:

@Controller 
public class MyController { 
    @Autowired 
    Service service; 
} 

//default.jar 
@Service 
DefaultService implements Service { 
    public void print() { 
     System.out.println("printing DefaultService.print()"); 
    } 
} 

//plugin.jar not in classpath yet 
@Service 
@Primary 
MyNewService implements Service { 
    public void print() { 
     System.out.println("printing MyNewService.print()"); 
    } 
} 

// बेहतर जगह की कमी के लिए, मैं ContextListener

public class PluginContextLoaderListener extends org.springframework.web.context.ContextLoaderListener { 

     @Override 
     protected void customizeContext(ServletContext servletContext, 
             ConfigurableWebApplicationContext wac) { 
       System.out.println("Init Plugin"); 
       PluginManager pluginManager = PluginManagerFactory.createPluginManager("plugins"); 
       pluginManager.init(); 

        //Prints the MyNewService.print() method 
        Service service = (Service) pluginManager.getService("service"); 
        service.print();     
      } 
    } 

    <listener> 
      <listener-class>com.plugin.PluginContextLoaderListener</listener-class> 
    </listener> 

के बाद भी मैं classloader में जार लोड से प्लगइन जार भरी हुई, डिफ़ॉल्ट सेवा अभी भी सेवा के रूप में इंजेक्शन दी जा रही है। वसंत की डी लाइफसाइकिल में भाग लेने के लिए मुझे प्लगइन जार कैसे मिलता है?

संपादित: इसे बस रखने के लिए, मेरे पास एक युद्ध फ़ाइल है जिसमें युद्ध के अंदर प्लगइन निर्देशिका में कुछ प्लगइन जार हैं। ऐप चालू होने पर कॉन्फ़िगरेशन फ़ाइल से एक मान के आधार पर, मैं उस विशेष प्लगइन जार को लोड करना चाहता हूं और इसके साथ एप्लिकेशन चला सकता हूं। इस तरह, मैं किसी को भी युद्ध वितरित कर सकता हूं, और वे चुन सकते हैं कि कॉन्फ़िगरेशन मूल्य के आधार पर कौन सी प्लगइन चलाने के लिए सब कुछ दोबारा तैयार किया जा सकता है। यह समस्या है जिसे मैं हल करने की कोशिश कर रहा हूं।

उत्तर

7

ऐसा लगता है कि आपको वसंतउचित रूप से बनाने की आवश्यकता है। मुझे लगता है कि यह क्लासपाथ मिलिंग के बिना संभव है। सबसे महत्वपूर्ण बात यह है कि स्प्रिंग कॉन्फ़िगरेशन फ़ाइलों के क्लासपाथ के भीतर हैं। तो अपने सभी प्लगइन जार को WEB-INF/lib में रखें और पढ़ें।

चलिए कोर मॉड्यूल से शुरू करते हैं। हम इसे classpath*:META-INF/spring/*-corecontext.xml पर स्थित फ़ाइलों से ApplicationContext बनाने के लिए बना देंगे।

अब हम सभी प्लगइन्स को अपनी कॉन्फ़िगरेशन फ़ाइलों को कहीं और बना देंगे। अर्थात। 'myplugin1' में इसकी कॉन्फ़िगरेशन स्थान होगी: classpath*:META-INF/spring/*-myplugin1context.xml। और anotherplugin में classpath*:META-INF/spring/*-anotherplugincontext.xml पर कॉन्फ़िगरेशन होंगे।

जो आप देखते हैं एक संयोजन है।यदि आप चाहें तो भी subdirectiries उपयोग कर सकते हैं:

  • कोर: classpath*:META-INF/spring/core/*.xml
  • myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
  • anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml

क्या मायने रखती है कि स्थानों संबंध तोड़ना होना जरूरी है।

यह सब शेष ApplicationContext निर्माता को सही स्थान पारित करना है। वेब अनुप्रयोगों के लिए इसके लिए सही स्थान ContextLoaderListener का विस्तार करना होगा और customizeContext(ServletContext, ConfigurableWebApplicationContext) विधि को ओवरराइड करना होगा।

आपकी सभी कॉन्फ़िगरेशन फ़ाइल को पढ़ने के लिए शेष है (इसका स्थान सर्वलेट इनिट पैरामीटर के रूप में पारित किया जा सकता है)।

String locationPrefix = "classpath*:META-INF/spring/"; 
String locationSiffix = "/*.xml"; 

List<String> configLocations = new ArrayList<String>(); 
configLocations.add(locationPrefix + "core" + locationSiffix); 

List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration(); 
for (String pluginName : pluginsTurnedOn) { 
    configLocations.add(locationPrefix + pluginName + locationSiffix); 
} 

applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()])); 

इस तरह आप आसानी से क्या है और क्या स्प्रिंग ApplicationContext में लोड नहीं है का प्रबंधन कर सकते हैं: से आप config स्थानों की सूची का निर्माण करने की जरूरत है।

अद्यतन:

यह काम वहाँ एक और छिपा धारणा मैंने बनाया है कि मैं के बारे में समझाने के लिए अब हूँ बनाने के लिए। कोर मॉड्यूल के आधार पैकेज और प्रत्येक प्लगइन भी को विघटित करना चाहिए। यही कारण है कि यानी है:

  • com.mycompany.myapp.core
  • com.mycompany.myapp.myplugin1
  • com.mycompany.myapp.anotherplugin

इस तरह प्रत्येक मॉड्यूल <context:componet-scan /> उपयोग कर सकते हैं (JavaConfig के बराबर पर) आसानी से के लिए क्लासपाथ स्कैनिंग जोड़ने के लिए यह केवल है। कोर मॉड्यूल में किसी भी प्लगइन पैकेज की कोई पैकेज स्कैनिंग नहीं होनी चाहिए। प्लगइन्स को क्लासपाथ स्कैनिंग में अपने स्वयं के पैकेज जोड़ने के लिए ApplicationContext के कॉन्फ़िगरेशन का विस्तार करना चाहिए।

+0

धन्यवाद। ऐसा लगता है कि यह काम करेगा। लेकिन मैं वसंत एनोटेशन का भारी उपयोग कर रहा हूं, और प्लगइन जार में कोई वसंत xml नहीं है। मुझे आश्चर्य है कि एनोटेशन-संचालित निर्भरता इंजेक्शन के लिए उपर्युक्त करना उतना ही आसान होगा। – Langali

+0

आपके प्रत्येक प्लगइन में कुछ प्रकार की कॉन्फ़िगरेशन होनी चाहिए (@ मेरा विस्तारित उत्तर देखें)। यहां तक ​​कि यदि प्लगइन के पैकेज के केवल एक ही '' के साथ यह केवल एक छोटा एक्सएमएल है, तो यह ** ** प्लगइन ** और कहीं और नहीं होना चाहिए। यह प्लगइन है जो खुद को सर्वश्रेष्ठ जानता है और खुद को कॉन्फ़िगर करने के बारे में जानता है। कोर मॉड्यूल को प्लगइन्स अस्तित्व से अवगत नहीं होना चाहिए - इसे केवल उन्हें 'एप्लिकेशन कॉन्टेक्स्ट' में अपनी कॉन्फ़िगरेशन में शामिल होने की संभावना प्रदान करनी चाहिए। – Roadrunner

2

यदि आप सर्वर को पुनरारंभ करते हैं, तो मुझे कोई कारण नहीं दिखता कि आप वेब-आईएनएफ/lib में जार क्यों नहीं जोड़ सकते हैं और इसे क्लास्स्पैट में रखते हैं। कस्टम क्लास लोडर और संदर्भ श्रोता की सभी जटिलताएं दूर हो जाती हैं, क्योंकि आप इसे स्प्रिंग के नियंत्रण के तहत किसी भी अन्य वर्ग की तरह मानते हैं।

यदि आप ऐसा करते हैं क्योंकि आप WAR को खोलना या संशोधित नहीं करना चाहते हैं, तो इसे सर्वर/lib निर्देशिका में क्यों न डालें? सर्वर क्लास लोडर इसे लेने दें। यह सभी प्लगइन कक्षाओं को सभी तैनात ऐप्स पर उपलब्ध कराता है।

उत्तर इस बात पर निर्भर करता है कि अलग/प्लगइन निर्देशिका कितनी महत्वपूर्ण है। यदि यह समाधान की कुंजी है, और आप सर्वर की/lib निर्देशिका में JAR नहीं जोड़ सकते हैं, तो वह यही है। मुझे कुछ नहीं मिला है। लेकिन मुझे लगता है कि कम से कम उस समाधान को फिर से देखना उचित होगा जो आपको यह सुनिश्चित करने के लिए है कि आप जो चाहते हैं उसे पूरा करने का यही एकमात्र तरीका है।

+0

यह हमेशा एक विकल्प है। लेकिन विचार था कि युद्ध के भीतर कुछ प्रमाणित प्लगइन्स (लेकिन क्लासपाथ में नहीं) थे, और उपयोगकर्ता ऐप के बाहर कॉन्फ़िगरेशन मान के आधार पर एक प्लगइन या दूसरा लोड कर सकते थे। इस तरह, एक औसत उपयोगकर्ता को केवल कॉन्फ़िगरेशन प्रॉपर्टी की परवाह करना होगा, न कि प्लगइन सिस्टम के आंतरिक। – Langali

संबंधित मुद्दे