现在想做到的事,在主线程中使用 com.xli.Target version 1.0 在子线程中使用 com.xli.Target version 1.1
已知条件:
- CLASSPATH 里的 com.xli.Target 是版本1.0
- 有一个自定义的 classloader 用于加载 com.xli.Target 版本1.1
- 继承自URLClassloader
- 会优先加载自己URL里的path
URL url = new URL("file:///tmp/TargetClass-1.1.jar");
ClassLoader cl = new ChildFirstClassLoader(new URL[]{url}, App.class.getClassLoader());
因为CLASSPATH中包含的版本已经是 1.0, 所以主线程中只需要正常初始化,调用即可输出 1.0
Target target = new Target();
target.printVersion();
Class<?> newClass = Class.forName("com.xli.Target", true, cl);
Object newInstance = newClass.getDeclaredConstructor().newInstance();
Method method = newClass.getMethod("printVersion");
method.invoke(newInstance);
- DynamicTargetCaller 是一个 Interface, 被Target类实现
- 下面代码可以让App Classloader 载入了 DynamicTargetCaller,而其子classloader (ChildFirstClassLoader) 载入了 Target
- 由于子classloader读出的class可以访问母classloader读出的类,所以这个也能正常输出 1.1
Class<?> newClass = Class.forName("com.xli.Target", true, cl);
DynamicTargetCaller newInstance = (DynamicTargetCaller) newClass.getDeclaredConstructor().newInstance();
newInstance.printVersion();
- 同一个Class(com.xli.Target)被两个classloader加载
- 而且相互之间还有赋值。
会产生
java.lang.ClassCastException
Caused by: java.lang.RuntimeException: java.lang.ClassCastException: class com.xli.Target cannot be cast to class com.xli.Target (com.xli.Target is in unnamed module of loader com.xli.ChildFirstClassloader @44e81672; com.xli.Target is in unnamed module of loader 'app')
Class<?> newClass = Class.forName("com.xli.Target", true, cl);
Target newInstance = (Target) newClass.getDeclaredConstructor().newInstance();
newInstance.printVersion();
重点想问的问题,为啥这样不行
这样子输出的还是版本 1.0
Thread.currentThread().setContextClassLoader(cl);
Target newInstance = new Target();
newInstance.printVersion();