Java 9,ClassLoader.getSystemClassLoader的兼容性问题

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)

Nic*_*lai 6

您已经遇到了一个事实,即系统类加载器不再是URLClassLoader。如ClassLoader::getSystemClassLoader返回值所示,这是一个实现细节,尽管依赖于不可忽略的代码量。

从注释来看,您正在寻找一种在运行时动态加载类的方法。正如艾伦·贝特曼(Alan Bateman)指出的那样,在Java 9中,无法通过追加到类路径来完成此操作。

您应该考虑为此创建一个新的类加载器。这具有附加的优势,因为新类没有加载到应用程序类加载器中,因此您将能够摆脱它们。如果您使用Java 9进行编译,则应该仔细阅读各层 -它们为您提供了一个全新的抽象概念,用于加载全新的模块图。


Chr*_*ies 6

不久前我偶然发现了这个问题。与许多人一样,我使用了与问题中类似的方法

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.