2012-12-18 17 views
5

हाथ की स्थिति उतनी सरल नहीं है जितनी शीर्षक का संकेत मिलता है।मैं कैसे नियंत्रित करूं कि क्लासलोडर कक्षा को लोड करता है?

जावा 1.6_17 जेडब्ल्यूएस के माध्यम से चल रहा है।

मेरे पास एक कक्षा है, MyClass कहें और इसके उदाहरण सदस्य चर में से एक एक गलती तृतीय पक्ष लाइब्रेरी से एक प्रकार है जहां कक्षा प्रारंभिकता के दौरान यह गतिशील रूप से Class.forName(String) के साथ अपने कुछ वर्गों को लोड करने का प्रयास करता है। इन मामलों में से एक में गतिशील रूप से कॉल होता है: Class.forName("foo/Bar")। यह कक्षा का नाम बाइनरी नामों के लिए जेएलएस का पालन नहीं करता है और अंत में java.lang.NoClassDefFoundError: foo/Bar तक जाता है।

हमारे पास एक कस्टम ClassLoader है जिसे मैंने ClassLoader.findClass(String) और ClassLoader.loadClass(String) पर एक sanitize विधि जोड़ा है जो इस समस्या को हल करता है। myCustomClassLoader.findClass("foo/Bar")

कौन सा तो बिना किसी समस्या के वर्ग लोड करता है:

मैं की तरह सामान कॉल कर सकते हैं। लेकिन अगर मैं समय से पहले कक्षा को लोड करता हूं, तो भी मुझे बाद में अपवाद मिलता है। ऐसा इसलिए है क्योंकि MyClass की शुरुआत के दौरान Bar - को उनके कोड को किसी स्थिर ब्लॉक में Class.forName("foo/Bar") पर कॉल करना समाप्त होता है। यह वास्तव में ठीक होगा अगर क्लासलोडर इसका उपयोग करने का प्रयास कर रहा था तो मेरा कस्टम क्लास लोडर था। लेकिन यह नहीं है। यह com.sun.jnlp.JNLPClassLoader है जो इस तरह की स्वच्छता नहीं करता है, इस प्रकार मेरी समस्या।

मैंने यह सुनिश्चित किया है कि Thread.currentThread().getContextClassLoader() मेरे कस्टम क्लास लोडर पर सेट है। लेकिन यह (जैसा कि आप जानते हैं) का कोई प्रभाव नहीं पड़ता है। मैंने इसे पढ़ने वाली कुछ चीजों के कारण main() में पहली चीज़ के रूप में भी सेट किया है, MyClass.class.getClassLoader() - जेएनएलपी क्लासलोडर है। अगर मैं इसे जेएनएलपी क्लासलोडर नहीं बन सकता और इसके बजाय मेरा उपयोग करने के लिए मजबूर कर सकता हूं, समस्या हल हो जाती है।

मैं क्लासलोडर को कक्षा प्रारंभ करने के दौरान क्लास लोड करने के लिए किस क्लासलोडर का उपयोग अपनी स्थिर कक्षा के माध्यम से कक्षा लोड करने के लिए किया जाता है .forName ("foo/bar") कॉल प्रारंभिकरण के दौरान किया गया है? मुझे विश्वास है कि अगर मैं अपने कस्टम क्लास लोडर को वापस करने के लिए MyClass.class.getClassLoader() को मजबूर कर सकता हूं, तो मेरी समस्या हल हो जाएगी।

यदि किसी के पास विचार है तो मैं अन्य विकल्पों के लिए खुला हूं।

टीएल; डीआर: मेरी पसंद के क्लासलोडर का उपयोग करने के लिए MyClass द्वारा संदर्भित सभी Class.forName(String) कॉल को तीसरी पार्टी लाइब्रेरी में कॉल करने में सहायता करें।

+3

* "एक गलती तृतीय पक्ष लाइब्रेरी से" * अंततः सबसे अच्छी रणनीति, उस एपीआई को प्रतिस्थापित करना है। –

+1

@AndrewTompson यह उस पर आ सकता है। मुझे आशा है कि यह नहीं है। तो अगर किसी के पास कोई विचार है - मैं सभी कान हूँ! –

+2

मैंने बूटस्ट्रैप क्लास बनाने और वहां से हमारे सभी बाहरी पुस्तकालयों को लोड करने के लिए URLClassLoader का उपयोग करके कुछ ऐसा किया। बूस्टरपैप क्लास लॉन्च होने पर वे क्लासपाथ में नहीं हैं, इसलिए वे देखे जाने वाले एकमात्र क्लासलोडर कस्टम हैं। कोई जवाब नहीं है क्योंकि मुझे नहीं पता कि यह जेडब्ल्यूएस के साथ काम करेगा या नहीं। हमने इसे रनटाइम पर प्लगइन लाइब्रेरी लोड करने के लिए किया था। – Thomas

उत्तर

1

मुझे लगता है कि हर किसी ने समस्या का जवाब देने के लिए अच्छे ठोस प्रयास किए हैं। हालांकि, यह पता चला है कि मैंने समस्या का गलत निदान किया है।

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

हम ओपनजेडीके प्राप्त कर चुके हैं क्योंकि जेडीके को खरोंच से दोबारा जोड़ना कुल दुःस्वप्न है (हमने कोशिश की)। ओपनजेडीके हमारे उत्पाद के साथ काम करने और JNLPClassLoader के माध्यम से डिबगिंग करने के बाद - यह पता चला है कि यह अभी भी पहले से ही पुराने पुराने .jnlp का उपयोग कर रहा था, जिसमें संसाधन पथ गलत था और इस प्रकार यह कक्षा को क्यों नहीं मिला।

हम उलझन में थे कि यह अभी भी प्राचीन .jnlp का उपयोग क्यों कर रहा था, भले ही हमने सही .jnlp के साथ सर्वर को कई बार सही ढंग से दोबारा तैनात किया था और चलाने के दौरान हमारे क्लाइंट एप्लिकेशन में बहुत से कोड परिवर्तन दिखाई देते थे।

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

आप चलाते हैं: javaws -uninstall क्लाइंट मशीन तो है कि .jnlp कैश और अगली बार यह सही .jnlp फ़ाइल का उपयोग करेगा साफ हो जाएगा पर।

वास्तव में दुखी है कि यह समस्या थी। उम्मीद है कि यह किसी और के निराशा के अंतहीन घंटे बचाता है जैसे कि यह हमें पैदा करता है।

4

यह मुझे एक लेख की याद दिलाता है जिसे मैंने 10 साल पहले जावा में क्लासलोडिंग व्यवस्था के बारे में पढ़ा था। यह अभी भी on JavaWorld है।

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

MyClass की अनुमति दे अन्य एक classloader द्वारा लोड करने के लिए तुम्हारा की तुलना में है कि classloader के लिए तत्काल वर्ग से एक रिश्ता (getClassLoader के माध्यम से), किसी भी संदर्भित वर्गों found at compile time को खोजने के लिए प्रयास करने के लिए प्रभावी रूप से दरकिनार कि अन्य classloader उपयोग करने के लिए की दुकान और कारण होगा जावा अपने क्लास लोडर पदानुक्रम और प्रतिनिधिमंडल मॉडल के आधार पर कस्टम क्लास लोडर। यदि MyClass इसके बजाय आपके वर्ग लोडर द्वारा परिभाषित किया गया है, तो आपको दूसरा मौका मिलता है।

यह URLClassLoader जैसे कुछ के लिए नौकरी की तरह लगता है, loadClass ओवरराइडिंग और अपने JAR में रहने वाले वर्गों के लिए प्रतिनिधिमंडल मॉडल को ट्रम्पिंग करता है। आप शायद एक कस्टम एंट्रीपॉइंट क्लास को अपने कस्टम क्लास लोडर के माध्यम से लोड करने के लिए मजबूर करने के लिए बूटस्ट्रैप दृष्टिकोण (जैसा कि उपरोक्त टिप्पणी में थॉमस द्वारा सुझाया गया है) का उपयोग करना चाहते हैं, अन्य सभी को इसके साथ खींचें।

उसी व्यक्ति द्वारा this other JavaWorld article भी जानकारीपूर्ण है, जो आपको क्लास.forनाम के चेतावनी के बारे में चेतावनी देता है। वह भी आपकी कक्षा लोड करने की व्यवस्था कर सकता है।

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

+0

+1 उत्कृष्ट जानकारी। मैं कल इसे पचाना होगा। अगर यह बाहर निकलता है, तो मैं इसे उत्तर के रूप में चिह्नित करूंगा। धन्यवाद! –

+0

आपका शोध कहां से निकला? कोई ब्रेक? –

2

यदि आप ClassLoader स्वयं को पैच करने के साथ विचारों से बाहर निकलते हैं, तो आप लाइब्रेरी बाइटकोड स्वयं को फिर से लिखने पर विचार कर सकते हैं - बस सही मूल्य के साथ "foo/bar" स्थिरता को प्रतिस्थापित करें, और फिर आपको अनुकूलित करने की आवश्यकता नहीं है आगे की कक्षा लोडिंग बिल्कुल!

आप इसे रनटाइम या पहले से ही कर सकते हैं।

+0

+1 उत्कृष्ट विचार।हालांकि जैसा कि मैंने अपने प्रश्न में उल्लेख किया है (और स्पष्ट करने की कोशिश की लेकिन असफल - क्षमा करें!) यह गतिशील वर्ग लोडिंग कर रहा है। मैंने पहले से ही सोर्स कोड पकड़ा और "फू/बार" के लिए तैयार किया और जो कुछ आया वह आयात (और वर्ग खुद ही डीफ) था। तो वर्ग का नाम स्थिर नहीं है। यह समस्या एक साल पहले इस सटीक पुस्तकालय के साथ हुई थी। यह हमारे कस्टम क्लासलोडर का उपयोग कर रहा था, और इसके माध्यम से डीबग करने के बाद मैंने देखा कि यह गतिशील तारों के साथ 'class.forName()' को कॉल कर रहा था और इसलिए मैंने कक्षा के नाम को स्वच्छ करने के लिए हमारे कस्टम क्लासलोडर को संशोधित किया और यह तब तक समस्या को ठीक कर दिया जब तक यह हुआ। –

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