تغيير classloader
-
26-09-2019 - |
سؤال
أحاول تبديل محمل الفصل في وقت التشغيل:
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();
}
}
(حقن classloader هل فصل يمتد org.apache.bcel.util.classloader التي ينبغي تحميل الإصدارات المعدلة من الفئات قبل أن تسأل عن الوالدين لهم)
أرغب في تحقيق نتيجة "النهج 1" و "النهج 2" نفسه تمامًا ، لكن يبدو الأمر كذلك thread.setContextClassloader (classloader) لا يفعل شيئًا ويستخدم "النهج 2" دائمًا جهاز تحميل فئة النظام (يمكن تحديده من خلال مقارنة متغيرات TCL و CCL أثناء تصحيح الأخطاء).
هل من الممكن القيام به الكل الفئات التي تم تحميلها بواسطة مؤشر ترابط جديد استخدم ClassLoader؟
المحلول
الفصل المجهول الذي تقوم بإنشائه عبر 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();
نصائح أخرى
أعتقد أن enjectingClassloader قد يكون مهمًا هنا. تذكر كيف يعمل Devlovation Classloading - إذا كان بإمكان أكثر من جهاز تحميل من الدرجة الأولى في التسلسل الهرمي العثور على الفصل ، فسيكون قائد الفصل الدراسي الأعلى هو الذي يتم تحميله. (انظر الشكل 21.2 هنا)
نظرًا لأن enjectingClassloader لا يحدد أحد الوالدين في مُنشئه ، فسيقوم بالتخلف عن المُنشئ في جهاز تحميل الفئة التجريدي ، والذي سيعين جهاز تحميل classloader الحالي كمصلح لـ enjectingClassLoader. لذلك ، نظرًا لأن الوالد (Context ClassLoader) يمكنه العثور على TestProxy ، فإنه دائمًا ما يحمل الفصل قبل أن يحصل حقن classloader على فرصة.