Java, Classpath, Classloading => एक ही जार / प्रोजेक्ट के कई संस्करण


117

मुझे पता है कि यह अनुभवी कोडर्स के लिए एक मूर्खतापूर्ण प्रश्न हो सकता है। लेकिन मेरे पास एक पुस्तकालय (एक http क्लाइंट) है जो मेरी परियोजना में उपयोग किए जाने वाले कुछ अन्य ढांचे / जार की आवश्यकता है। लेकिन उन सभी को विभिन्न प्रमुख संस्करणों की आवश्यकता होती है जैसे:

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

क्या क्लास लोडर बुद्धिमान है जो उन्हें किसी तरह अलग कर सकता है? न होने की सम्भावना अधिक? क्लास लोडर इसे कैसे संभालता है, यदि तीनों जार में एक क्लास समान है। कौन सा लोड किया गया है और क्यों?

क्या क्लास लोडर केवल एक जार को पिक करता है या वह कक्षाओं को मनमाने ढंग से मिलाता है? इसलिए उदाहरण के लिए यदि एक वर्जन को संस्करण -१.जर से लोड किया जाता है, तो एक ही क्लास लोडर से लोड की गई अन्य सभी कक्षाएं एक ही जार में चली जाएंगी?

आप इस समस्या को कैसे संभालेंगे?

क्या किसी तरह "जरुर" को जार में "समाहित" करने के लिए कुछ चाल है ताकि "किसी एक इकाई / पैकेज" के रूप में देखा जाए Classloader, या किसी तरह जुड़ा हो?

जवाबों:


57

क्लास लोडर संबंधी समस्याएं काफी जटिल मामला है। आपको किसी भी मामले में कुछ तथ्यों को ध्यान में रखना चाहिए:

  • एक आवेदन में क्लास लोडर आमतौर पर एक से अधिक होते हैं। बूटस्ट्रैप क्लास लोडर उपयुक्त को दर्शाता है। जब आप किसी नए वर्ग को इंस्टेंट करते हैं तो अधिक विशिष्ट क्लास लोडर को आमंत्रित किया जाता है। यदि यह उस वर्ग का संदर्भ नहीं देता है जिसे आप लोड करने की कोशिश कर रहे हैं, तो यह अपने माता-पिता को सौंपता है, और इसी तरह, जब तक आप बूटस्ट्रैप क्लास लोडर को नहीं लेते। यदि उनमें से कोई भी उस वर्ग का संदर्भ नहीं देता है जिसे आप लोड करने का प्रयास कर रहे हैं तो आपको एक ClassNotFoundException मिलती है।

  • यदि आपके पास एक ही बाइनरी नाम के साथ दो कक्षाएं हैं, तो उसी क्लास लोडर द्वारा खोजा जा सकता है, और आप जानना चाहते हैं कि उनमें से कौन सा आप लोड कर रहे हैं, तो आप केवल उस तरीके का निरीक्षण कर सकते हैं जो विशिष्ट क्लास लोडर वर्ग नाम को हल करने का प्रयास करता है।

  • जावा लैंग्वेज स्पेसिफिकेशन के अनुसार, क्लास बाइनरी नाम के लिए एक विशिष्ट बाधा नहीं है, लेकिन जहां तक ​​मैं देख सकता हूं, यह प्रत्येक क्लास लोडर के लिए अद्वितीय होना चाहिए।

मैं एक ही बाइनरी नाम के साथ दो वर्गों को लोड करने के तरीके का पता लगा सकता हूं, और इसमें दो अलग-अलग क्लास लोडर द्वारा डिफ़ॉल्ट व्यवहार को ओवरराइड करके उन्हें लोड किया गया है (और उनकी सभी निर्भरताएं) शामिल हैं। एक मोटा उदाहरण:

    ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

मैंने हमेशा क्लास लोडर अनुकूलन को एक मुश्किल काम पाया। यदि संभव हो तो मैं कई असंगत निर्भरता से बचने का सुझाव देता हूं।


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

5
नहीं - आम तौर पर एक क्लास लोडर अपने वर्ग की तलाश करने से पहले अपने माता-पिता को सौंपता है। क्लास लोडर के लिए कक्षा javadoc देखें।
जो किर्नी

1
मुझे लगता है कि
टॉमकट

@ डॉकिंगराज: कुछ गोलगप्पे के बाद मैंने इसे ओरेकल डॉक्स से पाया: "डेलिगेशन डिज़ाइन में, एक क्लास लोडर एक क्लास को लोड करने का प्रयास करने से पहले अपने माता-पिता को क्लास लोडिंग सौंपता है। [...] अगर पैरेंट क्लास लोडर एक क्लास लोड कर सकता है, तो। वर्ग लोडर वर्ग को स्वयं लोड करने का प्रयास करता है। वास्तव में, एक वर्ग लोडर केवल उन कक्षाओं को लोड करने के लिए जिम्मेदार है जो माता-पिता के लिए उपलब्ध नहीं हैं "। मैं आगे जांच करूंगा। यदि यह डिफ़ॉल्ट कार्यान्वयन के रूप में उभरेगा, तो मैं तदनुसार प्रतिक्रिया को अपडेट करूंगा। ( docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html )
Luca Putzu

20

प्रत्येक कक्षा का भार ठीक एक कक्षा में होता है। आमतौर पर पहले वाला मिला।

OSGi का उद्देश्य एक ही जार के कई संस्करणों की समस्या को हल करना है। इक्विनॉक्स और अपाचे फेलिक्स ओएसजीआई के लिए सामान्य ओपन-सोर्स कार्यान्वयन हैं।


6

क्लास लोडर जार से कक्षाएं लोड करेगा जो पहले क्लासपाथ में हुआ था। आम तौर पर, लाइब्रेरी के असंगत संस्करणों में पैकेजों में अंतर होगा, लेकिन अप्रत्याशित स्थिति में वे वास्तव में असंगत होते हैं और उन्हें एक के साथ बदला नहीं जा सकता है - जारज़र आज़माएं।


6

क्लास लोडर की डिमांड पर क्लास लगाते हैं। इसका मतलब यह है कि आपके आवेदन और संबंधित पुस्तकालयों द्वारा पहले आवश्यक वर्ग को अन्य कक्षाओं से पहले लोड किया जाएगा; आश्रित कक्षाओं को लोड करने का अनुरोध आम तौर पर एक श्रेणी के लोडिंग और लिंकिंग प्रक्रिया के दौरान जारी किया जाता है।

आपको LinkageErrorयह कहते हुए सामना करने की संभावना है कि डुप्लिकेट वर्ग की परिभाषाएं क्लास लोडर के लिए सामने आई हैं, आमतौर पर यह निर्धारित करने का प्रयास नहीं किया जाता है कि किस वर्ग को पहले लोड किया जाना चाहिए (यदि लोडर के क्लासपाथ में मौजूद एक ही नाम के दो या अधिक वर्ग हैं)। कभी-कभी, क्लास लोडर क्लासपाथ में होने वाली पहली क्लास को लोड करेगा और डुप्लिकेट कक्षाओं को अनदेखा करेगा, लेकिन यह लोडर के कार्यान्वयन पर निर्भर करता है।

इस तरह की त्रुटियों को हल करने के लिए अनुशंसित अभ्यास पुस्तकालयों के प्रत्येक सेट के लिए एक अलग क्लास लोडर का उपयोग करना है जिसमें परस्पर विरोधी निर्भरताएं हैं। इस तरह, यदि कोई क्लास-लोडर किसी लाइब्रेरी से कक्षाएं लोड करने का प्रयास करता है, तो आश्रित वर्ग को उसी क्लास-लोडर द्वारा लोड किया जाएगा, जिसके पास अन्य पुस्तकालयों और निर्भरताओं तक पहुंच नहीं है।


1

आप URLClassLoaderजार के एक अलग -2 संस्करण से कक्षाओं को लोड करने के लिए आवश्यकता के लिए उपयोग कर सकते हैं :

URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("httpclient-v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
URLClassLoader loader2 = new URLClassLoader(new URL[] {new File("httpclient-v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");

Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();

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