Mos*_*bdo 8 java classloader urlclassloader java-9
下面的代码将jar文件添加到构建路径,它适用于Java 8.但是,它会抛出Java 9的异常,该异常与转换为URLClassLoader有关.有什么想法可以解决这个问题吗?最佳解决方案将对其进行编辑以与Java 8和9一起使用.
private static int AddtoBuildPath(File f) {
try {
URI u = f.toURI();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(urlClassLoader, u.toURL());
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | MalformedURLException | IllegalAccessException ex) {
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
您已经遇到了一个事实,即系统类加载器不再是URLClassLoader。如ClassLoader::getSystemClassLoader返回值所示,这是一个实现细节,尽管依赖于不可忽略的代码量。
从注释来看,您正在寻找一种在运行时动态加载类的方法。正如艾伦·贝特曼(Alan Bateman)指出的那样,在Java 9中,无法通过追加到类路径来完成此操作。
您应该考虑为此创建一个新的类加载器。这具有附加的优势,因为新类没有加载到应用程序类加载器中,因此您将能够摆脱它们。如果您使用Java 9进行编译,则应该仔细阅读各层 -它们为您提供了一个全新的抽象概念,用于加载全新的模块图。
不久前我偶然发现了这个问题。与许多人一样,我使用了与问题中类似的方法
private static int AddtoBuildPath(File f)
Run Code Online (Sandbox Code Playgroud)
在运行时动态添加到类路径的路径。问题中的代码在多个方面可能是糟糕的风格:1) 假设ClassLoader.getSystemClassLoader()返回 anURLClassLoader是一个未记录的实现细节,2) 使用反射addURL公开可能是另一个。
动态添加类路径的更简洁方法
如果您需要使用额外的类路径 URL 来通过“ Class.forName”加载类,一个干净、优雅和兼容(Java 8 到 10)的解决方案如下:
1)通过扩展URL类加载器编写自己的类加载器,有一个公共addURL方法
public class MyClassloader extends URLClassLoader {
public MyClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
Run Code Online (Sandbox Code Playgroud)
2)声明类加载器的(单例/应用程序范围)对象
private final MyClassloader classLoader;
Run Code Online (Sandbox Code Playgroud)
并通过
classLoader = new MyClassloader(new URL[0], this.getClass().getClassLoader());
Run Code Online (Sandbox Code Playgroud)
注意:系统类加载器是父类。加载的类虽然classLoader知道可以加载的类,this.getClass().getClassLoader()但不知道可以加载的类。
3)在需要时(动态)添加额外的类路径:
File file = new File(path);
if(file.exists()) {
URL url = file.toURI().toURL();
classLoader.addURL(url);
}
Run Code Online (Sandbox Code Playgroud)
4)通过你的单例类加载器实例化对象或你的应用程序
cls = Class.forName(name, true, classLoader);
Run Code Online (Sandbox Code Playgroud)
注意:由于类加载器在加载类(以及父类到其父类)之前尝试委托给父类加载器,因此您必须确保要加载的类对父类加载器不可见,以确保它是通过给定的类加载器加载。为了更清楚地说明这一点:如果你ClassPathB在你的系统类路径上ClassPathB有,然后在ClassPathA你的 custom 中添加了一些classLoader,那么下面的类ClassPathB将通过系统类加载器加载,并且下面的类ClassPathA不为他们所知。但是,如果您ClassPathB从系统类路径中删除,这些类将通过您的自定义加载classLoader,然后 ClassPathA 下的类对 ClassPathB 下的类是已知的。
5)您可以考虑通过以下方式将类加载器传递给线程
setContextClassLoader(classLoader)
Run Code Online (Sandbox Code Playgroud)
如果线程使用getContextClassLoader.