How can I implement a custom class loader in Java that dynamically loads and executes Java code at runtime?

huangapple go评论64阅读模式
英文:

How can I implement a custom class loader in Java that dynamically loads and executes Java code at runtime?

问题

我正在构建一个基于Java的应用程序,需要在运行时动态加载和执行插件类。每个插件由一个单独的Java类文件表示,实现了一个共同的接口Plugin。为了实现这一点,我需要实现一个自定义类加载器(PluginClassLoader),它可以从外部JAR文件中加载和实例化插件类。

我尝试在我的基于Java的应用程序中实现一个自定义类加载器,以便在运行时动态加载和执行插件类。我期望能成功地从外部JAR文件中加载和实例化插件类,并且它们可以在我的应用程序上下文中执行。

以下是我尝试的自定义类加载器示例:

public class PluginClassLoader extends ClassLoader {

    public Class<?> loadPluginClass(String pluginFilePath) throws IOException {
        byte[] pluginBytes = readPluginFile(pluginFilePath);
        return defineClass(null, pluginBytes, 0, pluginBytes.length);
    }

    private byte[] readPluginFile(String pluginFilePath) throws IOException {
        // 从外部JAR中读取插件文件的代码
    }
}

我期望loadPluginClass方法将从指定的文件路径加载插件类,并返回一个Class对象,然后我可以使用它来实例化和执行插件。然而,在测试时,我遇到了类加载问题,并且对我尝试加载的插件类出现了ClassNotFoundExceptions

英文:

I am building a Java-based application that needs to dynamically load and execute plugin classes at runtime. Each plugin is represented by a separate Java class file that implements a common interface Plugin. To achieve this, I need to implement a custom class loader (PluginClassLoader) that can load and instantiate plugin classes from external JAR files.

I tried implementing a custom class loader in my Java-based application to dynamically load and execute plugin classes at runtime. I expected the plugin classes to be successfully loaded and instantiated from external JAR files, and for them to be available for execution within my application's context.

Here's an example of my attemted custom class loader:

public class PluginClassLoader extends ClassLoader {

    public Class&lt;?&gt; loadPluginClass(String pluginFilePath) throws IOException {
        byte[] pluginBytes = readPluginFile(pluginFilePath);
        return defineClass(null, pluginBytes, 0, pluginBytes.length);
    }

    private byte[] readPluginFile(String pluginFilePath) throws IOException {
        // Code to read the plugin file from external JAR
    }
}

I expected that the loadPluginClass method would load the plugin class from the specified file path and return a Class object that I could then use to instantiate and execute the plugin. However, when I tested it, I encountered class loading issues and ClassNotFoundExceptions for the plugin classes that I tried to load.

答案1

得分: 1

你已经在自定义类加载器中添加了一个方法,用于根据自己的请求加载单个类。然而,如果插件由多个类组成,也就是说您试图加载的类依赖于插件内的其他类,JVM 将从您的加载器请求这些类。这些请求不会使用您的 loadPluginClass 方法,因为 JVM 无法知道它。相反,它将调用继承的方法 loadClass(String name, boolean resolve)

由于您既没有重写这个方法,也没有重写 findClass(String name) 方法(这将是首选方式),所以这个方法对于每个未经父加载器找到的类都会抛出 ClassNotFoundException。解析依赖关系失败可能会导致依赖类的失败,因此即使您最初调用 loadPluginClass 也可能在 defineClass 调用时出现 ClassNotFoundException

由于您说您在插件中使用了 jar 文件,根本不需要实现自己的 ClassLoader。只需使用已经存在的 URLClassLoader,并将 jar 文件的 URL 传递给构造函数。或者对其进行子类化,以能够在其上调用 addURL。请注意,将 File 转换为 URL 的正确方法是在 File 对象上调用 toURI().toURL() 而不是 toURL(),以避免路径名中的空格或其他特殊字符引起问题。

此外,请确保在主应用程序未通过默认应用程序类加载器加载时将主应用程序的加载器指定为父加载器。

英文:

You have added a method to your custom class loader to load a single class on your own request. However, if the plugin consists of more than one class, i.e. the class you’re trying to load has dependencies to other classes within the plugin, the JVM will request those classes from your loader. Those requests won’t use your loadPluginClass method which the JVM can’t know. Instead, it will invoke the inherited method loadClass(String name, boolean resolve).

Since you didn’t override neither this method nor the findClass(String name) method (which would be the preferred way), this method will throw a ClassNotFoundException for every class not found through the parent loader. A failure to resolve a dependency may cause a failure for the dependent class, so even your initial call to loadPluginClass can fail with a ClassNotFoundException at the defineClass call.

Since you said that you are using jar files for your plugins, there is no need to implement your own ClassLoader at all. Just use the already existing URLClassLoader and pass the URL of the jar file to the constructor. Or subclass it, to be able to call addURL on it. Mind that the correct way to convert a File to a URL is to call toURI().toURL() on the File object instead of toURL(), to avoid problems with spaces or other special characters in path names.

Further keep in mind to specify your main application’s loader as parent loader when the main application has not been loaded through the default application class loader.

huangapple
  • 本文由 发表于 2023年4月7日 05:42:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75953991.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定