2010-05-12 17 views
7

मैं कार्यावधि में वर्ग लोडर स्विच करने के लिए कोशिश कर रहा हूँ:बदलें classloader

public class Test { 
    public static void main(String[] args) throws Exception { 
     final InjectingClassLoader classLoader = new InjectingClassLoader(); 
     Thread.currentThread().setContextClassLoader(classLoader); 
     Thread thread = new Thread("test") { 
      public void run() { 
       System.out.println("running..."); 
       // approach 1 
       ClassLoader cl = TestProxy.class.getClassLoader(); 
       try { 
        Class c = classLoader.loadClass("classloader.TestProxy"); 
        Object o = c.newInstance(); 
        c.getMethod("test", new Class[] {}).invoke(o); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
       // approach 2 
       new TestProxy().test(); 
      }; 
     }; 
     thread.setContextClassLoader(classLoader); 
     thread.start(); 
    } 
} 

और:

public class TestProxy { 
    public void test() { 
     ClassLoader tcl = Thread.currentThread().getContextClassLoader(); 
     ClassLoader ccl = ClassToLoad.class.getClassLoader(); 
     ClassToLoad classToLoad = new ClassToLoad(); 
    } 
} 

(InjectingClassLoader एक वर्ग org.apache.bcel.util का विस्तार है .ClassLoader जो कक्षाओं के संशोधित संस्करणों को उनके लिए माता-पिता से पूछने से पहले लोड करना चाहिए)

मुझे पसंद आएगा ई "दृष्टिकोण 1" और "दृष्टिकोण 2" का परिणाम बिल्कुल समान बनाने के लिए, लेकिन ऐसा लगता है कि thread.setContextClassLoader (classLoader) कुछ भी नहीं करता है और "दृष्टिकोण 2" हमेशा सिस्टम क्लासलोडर का उपयोग करता है (टीसीएल की तुलना करके निर्धारित किया जा सकता है और डीबगिंग के दौरान सीसीएल चर)।

क्या सभी कक्षाओं को नए थ्रेड उपयोग द्वारा लोड किए गए वर्गों द्वारा लोड किया जा सकता है?

उत्तर

12

अज्ञात वर्ग जिसे आप new Thread("test") { ... } के माध्यम से बना रहे हैं, संलग्नक उदाहरण के लिए एक निहित संदर्भ है। इस अज्ञात वर्ग के भीतर कक्षा अक्षर को संलग्न वर्ग के क्लासलोडर का उपयोग करके लोड किया जाएगा।

इस परीक्षण कार्य को करने के लिए, आपको एक उचित रननेबल कार्यान्वयन निकालना चाहिए, और वांछित क्लासलोडर का उपयोग करके इसे प्रतिबिंबित करना चाहिए; फिर उस धागे को स्पष्ट रूप से पास करें। की तरह कुछ:

public final class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("running..."); 
      // etc... 
     } 
    } 

    final Class runnableClass = classLoader.loadClass("classloader.MyRunnable"); 
    final Thread thread = new Thread((Runnable) runableClass.newInstance()); 

    thread.setContextClassLoader(classLoader); // this is unnecessary unless you you are using libraries that themselves call .getContextClassLoader() 

    thread.start(); 
+1

नाइट: "प्रतिबिंबित"। यह देखते हुए कि संदर्भ वर्ग लोडर के बारे में प्रश्न में भ्रम है, आप शायद उल्लेख कर सकते हैं कि setContextClassLoader का कोई प्रभाव नहीं है जब तक कि थ्रेड कुछ ऑपरेशन नहीं कर रहा है जिसके लिए इसे सेट करने की आवश्यकता होती है (उदा।, एक SAXParser बनाना, JNDI लुकअप करना आदि)। –

+0

फिक्स्ड; reflexively -> परावर्तक रूप से। धन्यवाद। –

1

मुझे लगता है कि इंजेक्शन क्लासलोडर यहां महत्वपूर्ण हो सकता है। याद रखें कि वर्गीकरण वर्गीकरण कैसे काम करता है - यदि आपके पदानुक्रम में एक से अधिक क्लासलोडर कक्षा को पा सकते हैं, तो सबसे ज्यादा क्लासलोडर लोड होगा। (चित्र 21.2 here देखें)

इंजेक्शन क्लासलोडर अपने कन्स्ट्रक्टर में माता-पिता को निर्दिष्ट नहीं करता है, यह सार क्लासलोडर में कन्स्ट्रक्टर के लिए डिफ़ॉल्ट होगा, जो मौजूदा संदर्भ क्लासलोडर को इंजेक्शन क्लासलोडर के माता-पिता के रूप में सेट करेगा। इसलिए, चूंकि अभिभावक (पुराना संदर्भ क्लासलोडर) टेस्टप्रॉक्सी पा सकता है, इसलिए इंजेक्शन क्लासलोडर को एक मौका मिलने से पहले यह कक्षा को हमेशा लोड करता है।

+0

ठीक है, खेद ... यह जिसके साथ कुछ बुरा सामान बनाता है तरीका है कि InjectingClassLoader एक वर्ग (कार्यान्वित modifyClass विधि के साथ) org.apache.bcel.util.ClassLoader का विस्तार है में प्रासंगिक है इसे लोड करने से पहले वर्ग। AFAIK org.apache.bcel.util.ClassLoader क्लासलोडर श्रृंखला के डिफ़ॉल्ट व्यवहार को ओवरराइड कर रहा है जिस तरह से संशोधित वर्ग को पैरेंट क्लासलोडर के उपयोग से पहले लोड किया जाता है। – Chris

+0

बस स्रोत की जांच की और org.apache.bcel.utilClassloader अभी भी java.lang.ClassLoader को बढ़ाता है ... – Greg

+1

डिफ़ॉल्ट वर्ग लोडर कन्स्ट्रक्टर क्लासलोडर.getSystemClassLoader() का उपयोग करता है, Thread.currentThread() नहीं। GetContextClassLoader(), माता-पिता के रूप में कक्षा लोडर। –

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