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

时间:2017-10-21 12:31:11

标签: java class reflection jar classloader

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

这个代码从jar中加载一个类,与应用程序的.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$1.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.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.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)

我已经尝试了上面的代码和路径名/其他选项的许多变体,但每次都得到ClassNotFoundException。最初加载罐子时遇到了麻烦但是使用了固定的完全限定路径,现在我无法加载类。

有什么想法吗? :)

2 个答案:

答案 0 :(得分:1)

这是URLClassLoader的一个功能,它适用于类的目录(您可以将.jar视为打包目录)。

您使用的constructor具有以下javadoc

  

使用默认委托父ClassLoader为指定的URL构造一个新的URLClassLoader。首次在父类加载器中搜索后,将按照为类和资源指定的顺序搜索URL。任何以“/”结尾的URL都被假定为引用目录。否则,假定URL引用JAR文件,该文件将根据需要下载和打开。

如果您绝对想要为每个单独的类创建类加载器,则应考虑对SecureClassLoader进行子类化并使用defineClass方法手动定义新类。

The basics of Java class loaders文章对您有所帮助。

答案 1 :(得分:1)

再次看到我的问题并意识到我忘了发布我发现问题的解决方案。我希望这可以帮助将来的某个人,因为这是我遇到的唯一一个工作案例 - 大学任务问题。我简直不敢相信这很简单:

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