2017-10-21 114 views
0

试图为我的应用程序实现插件系统,其代码扩展了我的Plugin类以定义自定义功能。如何从运行时从工作目录加载.class从jar?

此代码从一个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。最初加载罐子时遇到问题,但使用固定的完全限定路径,现在我无法加载类。

任何想法? :)

+0

你真的应该使用[的ServiceLoader(https://docs.oracle.com/javase/9​​/docs/api/java/util/ServiceLoader.html)为了这。 – VGR

回答

0

再次看到我的问题,并意识到我忘了发布一个解决方案,我发现我的问题。我希望这能帮助未来的人,因为这是我发现的唯一一个我的情况 - 一个大学任务问题。我简直不敢相信这是这个简单:

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

这是URLClassLoader的一个功能,它与类的目录一起工作(您可以假设为.jar作为打包目录)。

constructor您使用具有以下的javadoc

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

如果你绝对要创建的类加载器为每个单独的类,你应该考虑继承SecureClassLoaderdefineClass方法手动定义新类。

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

+0

感谢您的建议。由于实施时间和时间限制的困难,我决定避免允许.class文件。 –