如何实现从WAR拉出的共享接口

时间:2013-12-18 02:41:44

标签: java tomcat servlets

我有一个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内部发现的类之间的某种差异,并提取类文件。但是,因为我所做的所有提取类文件的方法都是将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);
}
...
}

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

1 个答案:

答案 0 :(得分:1)

由不同类加载器定义的类不同

  

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

在这种情况下,zipLoader会返回实现其他 MyPlugin的{​​{1}}实例(它也是从zip加载的吗?):

ServicePluginInterface

似乎应用程序服务器已经定义了(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance(); ,因此您无需重新部署它。将所需文件(ServicePluginInterface等)添加为项目的非部署依赖项应该足够了。

另一种方法是依靠事实,并通过反射访问ServicePluginInterface中的方法(使用ServicePluginInterface返回的Class对象,而不是zipLoader。 / p>