Skip to content

Instantly share code, notes, and snippets.

@risyomei
Created June 1, 2023 14:13
Show Gist options
  • Save risyomei/057e07213fa4debb3bca7e5eb5141bd0 to your computer and use it in GitHub Desktop.
Save risyomei/057e07213fa4debb3bca7e5eb5141bd0 to your computer and use it in GitHub Desktop.

现在想做到的事,在主线程中使用 com.xli.Target version 1.0 在子线程中使用 com.xli.Target version 1.1

已知条件:

  1. CLASSPATH 里的 com.xli.Target 是版本1.0
  2. 有一个自定义的 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();

子线程

证明可行的方法1: 最常规的反射


Class<?> newClass = Class.forName("com.xli.Target", true, cl);
Object newInstance = newClass.getDeclaredConstructor().newInstance();
Method method = newClass.getMethod("printVersion");
method.invoke(newInstance);

证明可行的方法2:

  • 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();

不可行的方法1:

  • 同一个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();

不可行的方法2:

重点想问的问题,为啥这样不行

这样子输出的还是版本 1.0

Thread.currentThread().setContextClassLoader(cl);
Target newInstance = new Target();
newInstance.printVersion();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment