在Java 9下运行时将jar添加到类路径

21

直到,所有人都使用以下方式在运行时以编程方式将外部jar添加到类路径中:

URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
method.invoke(sysloader, new Object[]{file.toURI().toURL()});

现在使用Java9出现了问题:

线程"main"中的异常java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader 无法转换为java.base/java.net.URLClassLoader

URLClassLoader在Java 9中不再起作用。如何在jdk9下编程运行时将外部jar添加到类路径中?

3
尽管发布说明中写道,当前的JavaSE/JDK在运行时没有提供任何API来增强类路径。但是,您越详细地阐述您最终想要达到的目标,社区就能够提供更多的帮助。 - Naman
2
ClassLoader.getSystemClassLoader()不再是一个URLClassLoader,但如果需要,您可以随时创建一个新的URLClassLoader实例。 - ZhekaKozlov
1
@ZhekaKozlov是正确的,特别是如果您可以通过反射调用JAR文件中的代码。 - Alan Bateman
实际上,使用Java 9,您有两个问题。即使ClassLoader实际上是URLClassLoader,我怀疑在模块化应用程序中尝试反射也不会被允许。 - VGR
我发现“Gradle”在UrlClassLoader方面存在问题。有人知道他们是如何解决的吗? - Evgeniy Egorov
@EvgeniyEgorov 由于您没有指定确切的用例,我们只是在猜测。但也许您实际上并不需要修改现有的类路径,而可以为新的JAR创建一个新的类加载器?请参见此答案 - Nicolai Parlog
2个回答

11

JavaSE9发布说明中有类似的内容:

应用程序类加载器不再是java.net.URLClassLoader的实例(这是以前版本中未指定的实现细节)。

代码假设ClassLoader::getSytemClassLoader返回一个URLClassLoader对象将需要更新。

请注意,Java SE和JDK不提供API供应用程序或库在运行时动态增补类路径

此外,当需要扩展类路径时,可以使用下面的方法:

Class<?> clazz = Class.forName("nameofclass", true, new URLClassLoader(urlarrayofextrajarsordirs));

如Oracle的该帖子所建议的。但这也有一些注意事项:

  • java.util.ServiceLoader使用线程的类加载器上下文Thread.currentThread().setContextClassLoader(specialloader);

  • java.sql.DriverManager尊重调用者的类加载器,而不是线程的类加载器。直接使用Class.forName("drivername", true, new URLClassLoader(urlarrayofextrajarsordirs).newInstance();创建驱动程序。

  • javax.activation使用线程的类加载器上下文(对于javax.mail很重要)。


好的。旧的换什么? - Evgeniy Egorov
@EvgeniyEgorov 实际上问题是你想要实现什么。如果你能详细说明,会更有帮助。 - Naman
我试图在谷歌搜索中找到相关信息,但很不幸地发现没有找到任何相关的信息或文档。 - Evgeniy Egorov
2
@EvgeniyEgorov 你可能也想看看这个帖子,并明确你的需求。 - Naman

3
Naman的答案并不能正确替代您所需要的内容。 在Java 9及以上版本中,将jar添加到类路径的正确方法是使用 Java InstrumentationappendToSystemClassLoaderSearch(JarFile jarfile)方法。
首先,您需要将Agent类添加到MANIFEST.MF中。 Launcher-Agent-Class:com.yourpackage.Agent 然后添加您的代理程序。 以下示例将允许您调用Agent.addClassPath(File f)在Java 8和9+中将Jar添加到类路径中。
public class Agent {
    private static Instrumentation inst = null;

    // The JRE will call method before launching your main()
    public static void agentmain(final String a, final Instrumentation inst) {
        Agent.inst = inst;
    }

    public static boolean addClassPath(File f) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();

        try {
            // If Java 9 or higher use Instrumentation
            if (!(cl instanceof URLClassLoader)) {
                inst.appendToSystemClassLoaderSearch(new JarFile(f));
                return;
            }

            // If Java 8 or below fallback to old method
            Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            m.setAccessible(true);
            m.invoke(cl, (Object)f.toURI().toURL());
        } catch (Throwable e) { e.printStackTrace(); }
    }

}

1
jdk12. agentmain(..) 没有被调用。MANIFEST.MF 中包含了 Launcher-Agent-Class 字符串。 - Evgeniy Egorov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接