如何在运行时从 jar 从工作目录加载 .class?

How to load .class from working directory at runtime from jar?

正在尝试为我的应用程序实现插件系统,其代码扩展了我的插件 class 以定义自定义功能。

此代码用于从 jar 中加载 class,在与应用程序的 .jar 相同的目录中,工作正常:

if(pluginName.endsWith(".jar"))
{
    JarFile jarFile = new JarFile(pluginName);
    Enumeration jarComponents = jarFile.entries();

    while (jarComponents.hasMoreElements())
    {
        JarEntry entry = (JarEntry) jarComponents.nextElement();
        if(entry.getName().endsWith(".class"))
        {
            className = entry.getName();
        }
    }

    File file = new File(System.getProperty("user.dir")+"/"+pluginName);
    URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()});

    Class newClass = Class.forName(className.replace(".class", ""), true, loader);
    newPlugin = (Plugin) newClass.newInstance();
}

这导致正确响应:

benpalmer$ java -jar assignment.jar test_subproject.jar
- INFO: Add new plugin: source.com

问题是我还想为用户提供指定 .class 文件的选项,因为这实际上是为了我的插件系统而打包到 .jar 中的所有内容。

else if(pluginName.endsWith(".class"))
{
    File file = new File(System.getProperty("user.dir")+"/"+pluginName);
    URLClassLoader loader = new URLClassLoader(new URL[]{file.toURI().toURL()});

    Class newClass = Class.forName(className.replace(".class", ""), true, loader);
    newPlugin = (Plugin) newClass.newInstance();
}   
... exception handling

结果如下:

benpalmer$ java -jar assignment.jar TestPlugin.class
SEVERE: Error: Plugin class not found. Class name: 
java.lang.ClassNotFoundException: 
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at newsfeed.model.Plugin.loadClassFromJarFile(Plugin.java:144)
    at newsfeed.controller.NFWindowController.initPlugins(NFWindowController.java:81)
    at newsfeed.controller.NewsFeed.run(NewsFeed.java:20)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access0(EventQueue.java:97)
    at java.awt.EventQueue.run(EventQueue.java:709)
    at java.awt.EventQueue.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

我已经尝试了上面的代码和路径 names/other 选项的许多变体,但每次都得到 ClassNotFoundException。最初加载 jars 时遇到问题,但使用完全限定路径修复了该问题,现在我无法加载 类.

有什么想法吗? :)

这是 URLClassLoader 的一项功能,它适用于 classes 的目录(您可以假设 .jar 为打包目录)。

您正在使用的 constructor 具有以下 javadoc

Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader. The URLs will be searched in the order specified for classes and resources after first searching in the parent class loader. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be downloaded and opened as needed.

如果您绝对想为每个单独的 class 创建 class 加载器,您应该考虑使用 subclassing SecureClassLoader 并手动定义新的 class defineClass 方法。

The basics of Java class loaders文章应该对你有帮助。

再次看到我的问题并意识到我忘记了 post 我找到的问题的解决方案。我希望这对将来的人有所帮助,因为这是我为我的情况找到的唯一可行的例子 - 大学作业问题。简直不敢相信这么简单:

byte[] classData = Files.readAllBytes(Paths.get(fileName));
Class<?> cls = defineClass(null, classData, 0, classData.length); 
return (PluginClassName)cls.newInstance();