如何在运行时从下载的 jar 文件加载未知类?

2024-01-29

我正在构建一个客户端服务器应用程序。在运行时,客户端应用程序从服务器应用程序加载 jar 文件并存储它。我将客户端和服务器应用程序都作为 jar 文件运行。我现在想要加载这个下载的 jar 文件中包含的类。

例如,我有一个接口 A 和一个实现 A 的类 B。客户端应用程序不知道类 B、它的名称,甚至它的存在。客户端应用程序启动后,客户端应用程序会下载一个 jar 文件,其中包含一个包含以下内容的 jar 文件: server/package/B.class 其中 server 和 package 是文件夹。

现在客户端应用程序应该使用以下代码从下载的 jar 文件中加载此类 B:

URL downloadURL = downloadFolder.toURI().toURL();
URL[] downloadURLs = new URL[] { ruleSetFolderURL };
URLClassLoader loader =
    new URLClassLoader(downloadURLs);
Class tmp = loadClass(server.package.B);

但后来我得到了ClassNotFoundException在最后一行。我必须先提取 jar 文件吗? jar 文件中的文件夹结构类似于服务器应用程序的 bin 目录中的文件夹结构。


要从实现某个接口的 jar 文件动态加载类,但您事先不知道该类是哪个类,并且 jar 文件本身没有指定任何默认的“插件”类,您可以迭代下载的 jar 并获取 jar 中包含的类列表,如下所示:

    /**
     * Scans a JAR file for .class-files and returns a {@link List} containing
     * the full name of found classes (in the following form:
     * packageName.className)
     *
     * @param file
     * JAR-file which should be searched for .class-files
     * @return Returns all found class-files with their full-name as a List of
     *         Strings
     * @throws IOException If during processing of the Jar-file an error occurred
     * @throws IllegalArgumentException If either the provided file is null, does 
     *                                  not exist or is no Jar file 
     */
    public List<String> scanJarFileForClasses(File file) throws IOException, IllegalArgumentException
    {
            if (file == null || !file.exists())
                    throw new IllegalArgumentException("Invalid jar-file to scan provided");
            if (file.getName().endsWith(".jar"))
            {
                    List<String> foundClasses = new ArrayList<String>();
                    try (JarFile jarFile = new JarFile(file))
                    {
                            Enumeration<JarEntry> entries = jarFile.entries();
                            while (entries.hasMoreElements())
                            {
                                    JarEntry entry = entries.nextElement();
                                    if (entry.getName().endsWith(".class"))
                                    {
                                            String name = entry.getName();
                                            name = name.substring(0,name.lastIndexOf(".class"));
                                            if (name.indexOf("/")!= -1)
                                                    name = name.replaceAll("/", ".");
                                            if (name.indexOf("\\")!= -1)
                                                    name = name.replaceAll("\\", ".");
                                            foundClasses.add(name);
                                    }
                            }
                    }
                    return foundClasses;
            }
            throw new IllegalArgumentException("No jar-file provided");
    }

一旦知道 jar 文件中包含的类,您需要加载每个类并检查它们是否实现所需的接口,如下所示:

    /**
     * <p>
     * Looks inside a jar file and looks for implementing classes of the provided interface.
     * </p>
     *
     * @param file
     * The Jar-File containing the classes to scan for implementation of the given interface
     * @param iface
     * The interface classes have to implement
     * @param loader
     * The class loader the implementing classes got loaded with
     * @return A {@link List} of implementing classes for the provided interface
     * inside jar files of the <em>ClassFinder</em>s class path
     *
     * @throws Exception If during processing of the Jar-file an error occurred
     */
    public List<Class<?>> findImplementingClassesInJarFile(File file, Class<?> iface, ClassLoader loader) throws Exception
    {
        List<Class<?>> implementingClasses = new ArrayList<Class<?>>();
        // scan the jar file for all included classes
        for (String classFile : scanJarFileForClasses(file))
        {
            Class<?> clazz;
            try
            {
                // now try to load the class
                if (loader == null)
                    clazz = Class.forName(classFile);
                else
                    clazz = Class.forName(classFile, true, loader);

                // and check if the class implements the provided interface
                if (iface.isAssignableFrom(clazz) && !clazz.equals(iface))
                    implementingClasses.add(clazz);
            }
            catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            }
        }
        return implementingClasses;
    }

因为您现在可以收集某个接口的所有实现,所以您可以通过简单地初始化一个新实例

public void executeImplementationsOfAInJarFile(File downloadedJarFile)
{
    If (downloadedJarFile == null || !downloadedJarFile.exists())
        throw new IllegalArgumentException("Invalid jar file provided");

    URL downloadURL = downloadedJarFile.toURI().toURL();
    URL[] downloadURLs = new URL[] { downloadURL };
    URLClassLoader loader = URLClassLoader.newInstance(downloadURLs, getClass().getClassLoader());
    try
    {
        List<Class<?>> implementingClasses = findImplementingClassesInJarFile(downloadedJarFile, A.class, loader);
        for (Class<?> clazz : implementingClasses)
        {
            // assume there is a public default constructor available
            A instance = clazz.newInstance();
            // ... do whatever you like here
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

请注意,此示例假设 A 是一个接口。如果在 Jar 文件中找不到实现类,则类加载器将加载 jar 文件,但不会发生对象的实例化。

进一步注意,提供父类加载器始终是一个好习惯 - 特别是使用 URLClassLoader。否则,可能会发生 Jar 文件中未包含的某些类可能丢失的情况,因此您将得到一个ClassNotFoundException尝试访问它们。这是由于类加载器使用的委托机制,它首先询问其父级是否知道所需类的类定义。如果是这样,该类将由父类加载;如果没有,该类将由创建的 URLClassLoader 加载。

请记住,可以使用不同的类加载器(对等类加载器)多次加载同一类。但是,尽管类的名称和字节可能相同,但由于使用了不同的类加载器实例,这些类并不兼容 - 因此尝试将类加载器 A 加载的实例强制转换为类加载器 B 加载的类型将会失败。

@Edit:修改代码以避免返回空值,而是抛出或多或少适当的异常。 @Edit2:由于我无法接受代码审查建议,因此我将审查直接编辑到帖子中

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在运行时从下载的 jar 文件加载未知类? 的相关文章

  • 在 Java 中连接和使用 Cassandra

    我已经阅读了一些关于 Cassandra 是什么以及它可以做什么的教程 但我的问题是如何在 Java 中与 Cassandra 交互 教程会很好 如果可能的话 有人可以告诉我是否应该使用 Thrift 还是 Hector 哪一个更好以及为什
  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • 如何使用 Java 和 Selenium WebDriver 在 C 目录中创建文件夹并需要将屏幕截图保存在该目录中?

    目前正在与硒网络驱动程序和代码Java 我有一种情况 我需要在 C 目录中创建一个文件夹 并在该文件夹中创建我通过 selenium Web 驱动程序代码拍摄的屏幕截图 它需要存储在带有时间戳的文件夹中 如果我每天按计划运行脚本 所有屏幕截
  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • 在 java 类和 android 活动之间传输时音频不清晰

    我有一个android活动 它连接到一个java类并以套接字的形式向它发送数据包 该类接收声音数据包并将它们扔到 PC 扬声器 该代码运行良好 但在 PC 扬声器中播放声音时会出现持续的抖动 中断 安卓活动 public class Sen
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐