Run*_*ekn 1 java classloader urlclassloader classnotfoundexception java-17
我有一个主要方法,用于创建自定义类加载器并用它实例化一个名为 Test 的类。
public class App {
public static void main(String[] args) throws Exception {
try {
Class.forName("com.mycompany.app2.Test2"); // We ensure that Test2 is not part of current classpath
System.err.println("Should have thrown ClassNotFound");
System.exit(1);
} catch (ClassNotFoundException e) {
// ignore
}
String jar = "C:\\experiments\\classloader-test2\\target\\classloader-test2-1.0-SNAPSHOT.jar"; // Contains Test2
URL[] classPaths = new URL[] { new File(jar).toURI().toURL() };
ClassLoader classLoader = new URLClassLoader(classPaths, App.class.getClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);
Class.forName("com.mycompany.app2.Test2", true, classLoader); // Check that custom class loader can find the wanted class
Test test = (Test) Class.forName("com.mycompany.app.Test", true, classLoader).getDeclaredConstructor().newInstance();
test.ex(); // This throws ClassNotFound for Test2
}
}
Run Code Online (Sandbox Code Playgroud)
然后,该类本身实例化另一个类,该类不是原始类路径的一部分,而是自定义类路径的一部分。
public class Test {
public void ex() {
new Test2().test();
}
}
Run Code Online (Sandbox Code Playgroud)
根据我对类加载器的理解,由于测试是使用自定义类加载器创建的,所以其中的任何类加载都应该使用相同的加载器完成。但事实似乎并非如此。
Exception in thread "main" java.lang.NoClassDefFoundError: com/mycompany/app2/Test2
at com.mycompany.app.Test.ex(Test.java:7)
at com.mycompany.app.App.main(App.java:28)
Run Code Online (Sandbox Code Playgroud)
我需要在 main 方法中做什么才能使 Test#ex 工作而不更改 Test?我正在使用 Java 17。
您创建URLClassLoaderusingApp.class.getClassLoader()作为父类加载器。因此,通过自定义类加载器加载的请求Test是通过父加载器解析的,最终得到与您在 main 方法中使用的\xe2\x80\x99d 完全相同的类Test.class。
您可以传递不同的父加载器,例如null表示引导加载器,以禁止Test通过父加载器解析类,但这会导致两种无益的情况之一
com.mycompany.app.Test如果自定义类加载器本身没有类,则加载尝试将会失败。
如果自定义类加载器有一个com.mycompany.app.Test类,即 inside classloader-test2-1.0-SNAPSHOT.jar,那么它将是Test与应用程序类加载器加载的 main 方法中引用的类不同的类。在这种情况下,类型转换(Test)将会失败。
换句话说,Test你的 main 方法引用的类根本不会受到另一个不相关的类加载器的影响。
有一种完全不同的方法可能在某些情况下有效。当您只想注入一个新类时,不要创建新的类加载器。
\nbyte[] code;\ntry(var is = new URL("jar:file:C:\\\\experiments\\\\classloader-test2\\\\target\\\\" +\n "classloader-test2-1.0-SNAPSHOT.jar!/com/mycompany/app2/Test2.class").openStream())\n{\n code = is.readAllBytes();\n}\n\nMethodHandles.lookup().defineClass(code);\n\nTest test = new Test();\ntest.ex();\nRun Code Online (Sandbox Code Playgroud)\n这会将类添加到当前类加载上下文中,因此后续链接将会成功。有以下收获:
\ndefine如果您知道需要哪些类以及按什么顺序,则可以通过多次调用添加所有类。将类添加到现有环境的一种完全不同的方法是通过 Java 代理,因为它们可以将 jar 文件添加到类路径。
\n