थ्रेड के संदर्भ वर्ग लोडर और सामान्य क्लास लोडर के बीच अंतर


242

एक सूत्र के संदर्भ वर्ग लोडर और एक सामान्य वर्ग लोडर के बीच क्या अंतर है?

यही है, अगर Thread.currentThread().getContextClassLoader()और getClass().getClassLoader()अलग-अलग वर्ग लोडर ऑब्जेक्ट वापस करते हैं, तो कौन सा उपयोग किया जाएगा?

जवाबों:


151

प्रत्येक वर्ग अन्य कक्षाओं को लोड करने के लिए अपने स्वयं के क्लास लोडर का उपयोग करेगा। तो अगर ClassA.classसंदर्भ ClassB.classतो ClassBकी classloader की classpath पर होने की जरूरत है ClassA, या उसके माता-पिता।

थ्रेड संदर्भ क्लास लोडर वर्तमान थ्रेड के लिए वर्तमान क्लास लोडर है। किसी ऑब्जेक्ट को किसी वर्ग से बनाया जा सकता है ClassLoaderCऔर उसके बाद उसके स्वामित्व वाले थ्रेड को पास किया जा सकता है ClassLoaderD। इस मामले में ऑब्जेक्ट को Thread.currentThread().getContextClassLoader()सीधे उपयोग करने की आवश्यकता होती है यदि वह संसाधनों को लोड करना चाहता है जो अपने स्वयं के क्लास लोडर पर उपलब्ध नहीं हैं।


1
आप ऐसा क्यों कहते हैं कि लोडर (या लोडर के माता-पिता) ClassBके वर्गपथ पर होना चाहिए ? क्या यह लोडर को ओवरराइड करने के लिए संभव नहीं है , जैसे कि यह तब भी सफलतापूर्वक लोड हो सकता है, जब यह अपने वर्गपथ पर नहीं है? ClassAClassAClassAloadClass()ClassBClassB
पचेरियर

8
वास्तव में, सभी क्लास लोडर में एक क्लासपैथ नहीं है। जब लिखा गया कि "क्लासबी को क्लासए के क्लास लोडर के क्लासपैथ पर होना चाहिए", मेरा मतलब था "क्लासबी को क्लास के क्लास लोडर द्वारा लोड करने की आवश्यकता है"। समय का 90% वे एक ही मतलब है। लेकिन अगर आप URL आधारित क्लास लोडर का उपयोग नहीं कर रहे हैं, तो केवल दूसरा मामला सत्य है।
डेविड रूसेल

" ClassA.classसंदर्भ ClassB.class" कहने का क्या मतलब है ?
jameshfisher

1
जब ClassA में ClassB के लिए एक आयात विवरण होता है, या यदि ClassA में कोई विधि होती है, जिसमें ClassB का स्थानीय चर होता है। यदि यह पहले से लोड नहीं है, तो लोड होने के लिए क्लासबी को ट्रिगर करेगा।
डेविड रूसेल

मुझे लगता है कि मेरी समस्या इस विषय से जुड़ी हुई है। आप मेरे विलाप के बारे में क्या सोचते हैं? मुझे पता है कि यह एक अच्छा पैटर्न नहीं है, लेकिन मेरे पास इसे ठीक करने का कोई अन्य विचार नहीं है: stackoverflow.com/questions/29238493/…
Marcin Erbel

109

यह मूल प्रश्न का उत्तर नहीं देता है, लेकिन जैसा कि प्रश्न अत्यधिक रैंक और किसी भी ContextClassLoaderप्रश्न के लिए जुड़ा हुआ है , मुझे लगता है कि संबंधित प्रश्न का उत्तर देना महत्वपूर्ण है जब संदर्भ वर्ग लोडर का उपयोग किया जाना चाहिए। संक्षिप्त उत्तर: संदर्भ श्रेणी लोडर का उपयोग कभी न करें ! लेकिन इसे getClass().getClassLoader()तब सेट करें जब आपको एक ऐसी विधि को कॉल करना होगा जो एक ClassLoaderपैरामीटर गायब है ।

जब एक वर्ग का कोड दूसरी कक्षा को लोड करने के लिए कहता है, तो उपयोग करने के लिए सही वर्ग लोडर कॉलर वर्ग (यानी getClass().getClassLoader()) के रूप में एक ही वर्ग लोडर है । यह उस तरह से है जब चीजें 99.9% काम करती हैं क्योंकि यह वही है जो जेवीएम खुद करता है जब आप पहली बार एक नए वर्ग की आवृत्ति का निर्माण करते हैं, एक स्थैतिक विधि का उपयोग करते हैं, या एक स्थिर क्षेत्र का उपयोग करते हैं।

जब आप परावर्तन का उपयोग करके एक वर्ग बनाना चाहते हैं (जैसे कि जब विन्यास योग्य नामांकित कक्षा को डिस्क्राइबलाइज़ या लोड करना हो), तो जो पुस्तकालय प्रतिबिंब करता है, उसे हमेशा अनुप्रयोगClassLoader से एक पैरामीटर के रूप में प्राप्त करके अनुप्रयोग को किस वर्ग लोडर का उपयोग करना चाहिए, यह पूछना चाहिए । आवेदन (जो सभी वर्गों को पता है कि निर्माण की आवश्यकता है) उसे पास करना चाहिए getClass().getClassLoader()

क्लास लोडर प्राप्त करने का कोई अन्य तरीका गलत है। यदि कोई लाइब्रेरी हैक का उपयोग करता है, जैसे कि Thread.getContextClassLoader(), sun.misc.VM.latestUserDefinedLoader()या sun.reflect.Reflection.getCallerClass()यह एपीआई में कमी के कारण एक बग है। मूल रूप से, Thread.getContextClassLoader()केवल इसलिए मौजूद है क्योंकि जिसने भी डिज़ाइन किया है ObjectInputStreamएपीआई ClassLoaderएक पैरामीटर के रूप में स्वीकार करना भूल गया , और इस गलती ने जावा समुदाय को आज तक परेशान किया है।

उस ने कहा, कई जेडीके कक्षाएं कुछ हैक का उपयोग करती हैं ताकि कुछ क्लास लोडर का उपयोग करने का अनुमान लगाया जा सके। कुछ एक का उपयोग करते हैं ContextClassLoader(जो विफल रहता है जब आप एक साझा थ्रेड पूल पर अलग-अलग ऐप चलाते हैं, या जब आप छोड़ते हैं ContextClassLoader null), तो कुछ स्टैक चलते हैं (जो तब विफल हो जाता है जब क्लास का डायरेक्ट कॉलर खुद एक पुस्तकालय होता है), कुछ सिस्टम क्लास लोडर का उपयोग करते हैं (जो ठीक है, जब तक इसे केवल कक्षाओं में उपयोग करने के लिए प्रलेखित किया जाता है CLASSPATH) या बूटस्ट्रैप क्लास लोडर, और कुछ उपरोक्त तकनीकों के अप्रत्याशित संयोजन का उपयोग करते हैं (जो केवल चीजों को और अधिक भ्रमित करता है)। इससे दांतों का बहुत रोना और छींटाकशी हुई है।

इस तरह के एक एपीआई का उपयोग करते समय, सबसे पहले, क्लास लोडर को एक पैरामीटर के रूप में स्वीकार करने वाली विधि का अधिभार खोजने की कोशिश करें । अगर कोई समझदार तरीका नहीं है, तो ContextClassLoaderपहले एपीआई कॉल को सेट करने का प्रयास करें (और बाद में इसे रीसेट करें):

ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    // call some API that uses reflection without taking ClassLoader param
} finally {
    Thread.currentThread().setContextClassLoader(originalClassLoader);
}

5
हां, यह उत्तर है जो मैं किसी से भी प्रश्न पूछूंगा।
मार्को टोपोलनिक 13

6
यह उत्तर लोडिंग कक्षाओं के लिए क्लास लोडर का उपयोग करने पर (उन्हें प्रतिबिंब या समान के माध्यम से त्वरित करने के लिए) केंद्रित है, जबकि इसका एक और उद्देश्य (और, वास्तव में, एकमात्र उद्देश्य जिसे मैंने कभी व्यक्तिगत रूप से उपयोग किया है) संसाधनों को लोड कर रहा है। क्या एक ही सिद्धांत लागू होता है, या क्या ऐसी परिस्थितियां हैं जहां आप कॉलर क्लास लोडर के बजाय संदर्भ वर्ग लोडर के माध्यम से संसाधन लाना चाहते हैं?
ईगोर हंस

90

Javaworld.com पर एक लेख है जो अंतर बताता है => आपको किस क्लास-क्लास का उपयोग करना चाहिए

(1)

थ्रेड संदर्भ क्लास लोडर क्लास लोडिंग डेलिगेशन स्कीम के चारों ओर एक बैक डोर प्रदान करते हैं।

उदाहरण के लिए JNDI को लें: इसकी हिम्मत rt.jar में बूटस्ट्रैप कक्षाओं द्वारा कार्यान्वित की जाती है (J2SE 1.3 के साथ शुरू), लेकिन ये कोर JNDI कक्षाएं स्वतंत्र विक्रेताओं द्वारा कार्यान्वित JNDI प्रदाताओं को लोड कर सकती हैं और संभवतः एप्लिकेशन के -क्लासपैथ में तैनात की जा सकती हैं। यह परिदृश्य एक पैरेंट क्लास लोडर (इस मामले में प्राइमर्डियल एक) को अपने बच्चे के क्लास लोडर (उदाहरण के लिए सिस्टम एक) को दिखाई देने वाले एक वर्ग को लोड करने के लिए कहता है। सामान्य J2SE प्रतिनिधिमंडल काम नहीं करता है, और मुख्य JNDI वर्गों को थ्रेड संदर्भ लोडर का उपयोग करने के लिए वर्कअराउंड करना है, इस प्रकार प्रभावी रूप से उचित प्रतिनिधिमंडल के विपरीत दिशा में क्लास लोडर पदानुक्रम के माध्यम से "टनलिंग" करना है।

(2) एक ही स्रोत से:

यह भ्रम संभवतः कुछ समय के लिए जावा के साथ रहेगा। किसी भी प्रकार के डायनेमिक रिसोर्स लोडिंग के साथ J2SE API लें और यह अनुमान लगाने की कोशिश करें कि यह किस लोडिंग स्ट्रैटेजी का उपयोग करता है। यहाँ एक नमूना है:

  • JNDI संदर्भ क्लास लोडर का उपयोग करता है
  • Class.getResource () और Class.forName () वर्तमान क्लास लोडर का उपयोग करें
  • JAXP संदर्भ क्लास लोडर (J2SE 1.4 के रूप में) का उपयोग करता है
  • java.util.ResourceBundle कॉलर के करंट क्लास लोडर का उपयोग करता है
  • Java.protocol.handler.pkgs सिस्टम प्रॉपर्टी के माध्यम से निर्दिष्ट URL प्रोटोकॉल हैंडलर को केवल बूटस्ट्रैप और सिस्टम क्लास लोडर में देखा जाता है
  • जावा सीरियलाइज़ेशन एपीआई डिफ़ॉल्ट रूप से कॉलर के वर्तमान क्लास लोडर का उपयोग करता है

जैसा कि यह सुझाव दिया गया है कि मुख्य JNDI वर्गों को थ्रेड संदर्भ लोडर का उपयोग करने के लिए वर्कअराउंड करना है, मुझे समझ में नहीं आया कि यह इस मामले में कैसे मदद करता है। हम अभिभावक वर्ग लोडर का उपयोग करके कार्यान्वयन विक्रेता कक्षाएं लोड करना चाहते हैं, लेकिन वे मूल क्लास लोडर को दिखाई नहीं देते हैं । तो हम माता-पिता का उपयोग करके उन्हें कैसे लोड कर सकते हैं, भले ही हम इस मूल क्लास लोडर को थ्रेड के संदर्भ क्लास लोडर में सेट करें।
सनी गुप्ता

6
@SAM, सुझाया गया वर्कअराउंड वास्तव में आप जो कह रहे हैं उससे काफी विपरीत है। यह मूल bootstrapवर्ग लोडर को संदर्भ वर्ग लोडर के रूप में सेट नहीं किया जा रहा है, बल्कि इसके साथ स्थापित किया जा रहा चाइल्ड systemक्लासपथ क्लास लोडर है ThreadJNDIकक्षाएं तो उपयोग सुनिश्चित करते हुए कर रहे हैं Thread.currentThread().getContextClassLoader()JNDI कार्यान्वयन कक्षाएं classpath पर उपलब्ध लोड करने के लिए।
रवि थपलियाल

"सामान्य J2SE प्रतिनिधिमंडल काम नहीं करता है", क्या मुझे पता है कि यह काम क्यों नहीं करता है? क्योंकि बूटस्ट्रैप ClassLoader केवल rt.jar से क्लास को लोड कर सकता है और एप्लिकेशन के -क्लासपथ से क्लास को लोड नहीं कर सकता है? सही?
युफेंग शेन

37

@ डेविड रसेल जवाब में जोड़ते हुए, कई कक्षा लोडर द्वारा कक्षाएं भरी जा सकती हैं।

समझें कि क्लास लोडर कैसे काम करता है।

Javarevisited में javin paul ब्लॉग से:

यहां छवि विवरण दर्ज करें

यहां छवि विवरण दर्ज करें

ClassLoader तीन सिद्धांतों का पालन करता है।

प्रतिनिधि सिद्धांत

एक वर्ग जावा में लोड किया जाता है, जब इसकी आवश्यकता होती है। मान लें कि आपके पास एक विशिष्ट विशिष्ट वर्ग है, जिसे Abc.class कहा जाता है, इस वर्ग को लोड करने का पहला अनुरोध Application ClassLoader पर आएगा, जो इसके मूल एक्सटेंशन ClassLoader को सौंप देगा, जो आगे Primordial या बूटस्ट्रैप वर्ग लोडर को सौंपता है

  • बूटस्ट्रैप ClassLoader rt.jar से मानक JDK क्लास फ़ाइलों को लोड करने के लिए ज़िम्मेदार है और यह जावा में सभी क्लास लोडर का अभिभावक है। बूटस्ट्रैप क्लास लोडर का कोई अभिभावक नहीं है।

  • एक्सटेंशन क्लासलोडर अपने माता-पिता, बूटस्ट्रैप के लिए वर्ग लोडिंग अनुरोध को दर्शाता है और यदि असफल होता है, तो वर्ग प्रपत्र jre / lib / ext निर्देशिका या java.ext.dirs सिस्टम संपत्ति द्वारा इंगित किसी अन्य निर्देशिका को लोड करता है

  • सिस्टम या एप्लिकेशन क्लास लोडर और यह CLASSPATH पर्यावरण चर, -classpath या -cp कमांड लाइन विकल्प, JAR के अंदर मैनिफेस्ट फ़ाइल की क्लास-पाथ विशेषता से एप्लिकेशन विशिष्ट कक्षाओं को लोड करने के लिए जिम्मेदार है।

  • एप्लीकेशन क्लास लोडर एक्स्टेंशन क्लासलॉडर का एक बच्चा है और इसे sun.misc.Launcher$AppClassLoaderक्लास द्वारा कार्यान्वित किया जाता है ।

नोट: बूटस्ट्रैप क्लास लोडर को छोड़कर , जो मूल भाषा में ज्यादातर C में लागू होता है, सभी जावा क्लास लोडर का उपयोग करके लागू किया जाता है java.lang.ClassLoader

दृश्यता सिद्धांत

दृश्यता सिद्धांत के अनुसार, चाइल्ड क्लासलोडर पेरेंट क्लासलोडर द्वारा लोड की गई कक्षा को देख सकता है , लेकिन इसके विपरीत सच नहीं है।

विशिष्टता सिद्धांत

इस सिद्धांत के अनुसार माता-पिता द्वारा लोड किए गए एक वर्ग को फिर से चाइल्ड क्लासलोडर द्वारा लोड नहीं किया जाना चाहिए

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