Java 如何实现一个从战争中拉出来的共享接口

Java 如何实现一个从战争中拉出来的共享接口,java,tomcat,servlets,Java,Tomcat,Servlets,我有一个web服务,我们称之为service.war。它实现了一个我们称之为ServicePluginInterface的接口。在service.war启动期间,它读入环境变量并使用它们搜索jar(MyPlugin.jar)。当它找到jar时,它会使用第二个环境变量在jar中加载插件。它加载的类如下所示: public class MyPlugin implements ServicePluginInterface {...} servlet尝试使用以下代码加载插件: try {

我有一个web服务,我们称之为service.war。它实现了一个我们称之为ServicePluginInterface的接口。在service.war启动期间,它读入环境变量并使用它们搜索jar(MyPlugin.jar)。当它找到jar时,它会使用第二个环境变量在jar中加载插件。它加载的类如下所示:

public class MyPlugin implements ServicePluginInterface {...}
servlet尝试使用以下代码加载插件:

try {
        if (pluginClass == null) {
            plugin = null;
        }
        else {
            ZipClassLoader zipLoader = new ZipClassLoader(Main.class.getClassLoader(), pluginJar);
            plugin = (ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
            plugin.getAccount(null,null);
        }
    } catch (Exception e) {
        ...
    }
诀窍是我没有ServicePluginInterface的源代码或jar。不想这么轻易放弃,我从service.war文件中取出了类文件。通过使用这些类文件作为依赖项,我能够在没有编译器警告的情况下构建MyPlugin。但是,当Tomcat实际执行时,上面的代码部分会生成一个运行时异常:

java.lang.ClassCastException: com.whatever.MyPlugin cannot be cast to com.whomever.ServicePluginInterface

作为第二个参考点,我还能够构造一个合成类加载器(使用相同类加载机制的单独java可执行文件。同样,由于我没有ServicePluginInterface的原始源代码,所以我使用了WAR中的类文件。第二个合成加载程序(如果愿意的话,也叫faux servlet)可以很好地加载MyPlugin。因此我假设Tomcat JVM似乎检测到某种类型的在WAR中找到的类和提取的类文件之间的差异。然而,由于我提取类文件所做的只是将WAR作为zip打开并将其复制出来,因此很难想象这可能是什么


Javier就删除ServicePluginInterface的定义提出了一个有用的建议,该解决方案的问题是servlet用于从jar加载插件的ZipClassLoader重写ClassLoader findClass函数以从jar中拉出类,如下所示:

protected Class<?> findClass(String name) throws ClassNotFoundException
{
ZipEntry entry = this.myFile.getEntry(name.replace('.', '/') + ".class");

if (entry == null) {
  throw new ClassNotFoundException(name);
}
...
}
受保护的类findClass(字符串名称)抛出ClassNotFoundException
{
ZipEntry entry=this.myFile.getEntry(name.replace(“.”,“/”)+“.class”);
if(条目==null){
抛出新的ClassNotFoundException(名称);
}
...
}

然后,类ZipClassLoader递归地从jar加载所有父对象和接口。这意味着,如果插件jar不包含ServicePluginInterface的定义,它将失败。

由不同类装入器定义的类是不同的

在运行时,可能需要几个具有相同二进制名称的引用类型 由不同的类加载器同时加载。这些类型可以是或 可能不表示相同的类型声明。即使两个这样的类型 表示相同的类型声明,它们被认为是不同的

在这种情况下,
zipLoader
返回实现另一个
ServicePluginInterface
MyPlugin
实例(是否也从zip加载):

应用程序服务器似乎已经有了
ServicePluginInterface
的定义,那么您就不需要重新部署它了。添加所需文件(
ServicePluginInterface
等)作为项目的未部署依赖项就足够了


另一种方法是接受事实,通过反射访问
ServicePluginInterface
中的方法(使用
zipLoader
返回的
Class
对象,而不是
ServicePluginInterface.Class
).

关于类加载器的非常有用的评论-我怀疑真正的诀窍就在这一领域。不幸的是,由于您之前不知道的原因,我相信这不会解决我的问题,因为ZipClassLoader的性质。我在问题的末尾添加了详细信息,可以很好地格式化它们,但缺少ZipClassLoader失败,除非接口也在JAR中找到。至于第二个建议,不幸的是,无法修改servlet loader代码,因为它是由第三方提供的,我没有源代码。因此,我将标记您的答案为已接受,因为它可能是更普遍适用和有用的解决方案实际上,我最后做的是用我自己的一个直接加载的类文件完全覆盖ZipClassLoader类文件(不打开JAR)插件类——当然,我当时也必须将其放在WAR中。丑陋的黑客,但它工作了……为什么ZipClassLoader不委托给它的父级?它是哪个实现?很不幸,我认为ZipClassLoader是servlet所有者的版权所有,所以我不能完整地共享它,但现在我想知道它是否是pos可以编写更好的ZipClassLoader并在战争中替换类文件。。。
(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();