2013-12-18 41 views
1

我有一個Web服務,我們將其稱爲service.war。它實現了一個我們稱之爲ServicePluginInterface的接口。在啓動service.war期間,它會讀取環境變量並使用它們來搜索jar(MyPlugin.jar)。當它找到jar時,它會使用第二個環境變量在jar中加載插件。它加載的類看起來是這樣的:如何實現從WAR中提取的共享接口

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罐子。不想放棄這麼容易,我把class文件從service.war文件中提取出來。通過使用這些類文件作爲依賴項,我能夠在沒有編譯器警告的情況下構建MyPlugin。然而,當實際的Tomcat執行時,代碼的部分上方產生一個運行時異常:

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

作爲參考的第二點,我還能夠構建合成的類加載器(單獨的Java可執行同樣,由於我沒有ServicePluginInterface的原始源代碼,所以我使用了WAR中的類文件,第二個是合成加載器,或者假的servlet,如果你願意,可以加載MyPlugin。我會假設Tomcat JVM似乎正在檢測WAR中發現的類與提取的類文件之間的某種區別,然而,因爲我所做的所有提取類文件都是以WAR作爲zip打開,把它們複製出來,很難想象它會是什麼。


哈維爾提出了有益的建議有關刪除ServicePluginInterface的定義,該解決方案的問題是,ZipClassLoader該servlet使用加載插件出來的罐子將覆蓋類加載器的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不包含ServicePluginInterface的定義,它將會失敗。由不同類加載器所定義

+0

爲什麼ZipClassLoader不能委託給它的父項?它是什麼實現? – Javier

+0

不幸的是,我認爲ZipClassLoader對servlet所有者有版權,所以我不能完全分享它,但現在我想知道是否可以編寫更好的ZipClassLoader並在WAR中替換類文件。 .. – OverclockedTim

回答

1

類是不同

在運行時,多個參考類型的具有相同的二進制名稱可以是 由不同類裝載器同時裝載。這些類型可能或 可能不表示相同的類型聲明。即使 這兩種類型表示相同的類型聲明,它們也被認爲是不同的。 JLS

zipLoader返回一個實現的MyPlugin實例這種情況下,其他ServicePluginInterface(它與拉鍊裝入太多):

(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance(); 

看來,應用服務器已經有了一個定義ServicePluginInterface,那麼你不需要重新部署它。只需添加所需的文件即可(ServicePluginInterface等)作爲您項目的未部署依賴項。

的另一種方法的推移與事實活的,和通過反射訪問在ServicePluginInterface方法(使用由zipLoader返回的Class對象,而不是ServicePluginInterface.class)。

+0

關於班級裝載者非常有幫助的評論 - 我懷疑真正的技巧在於某個地方。 不幸的是,由於ZipClassLoader的特性,我相信這並不能解決我的問題。我已經將問題的詳細信息添加到問題的結尾,可以對它們進行很好的格式化,但缺點是ZipClassLoader會失敗,除非在JAR中也找到該接口。 至於第二個建議,遺憾的是不能修改servlet加載器代碼,因爲它是由第三方提供的,我沒有源代碼。 – OverclockedTim

+0

因此,我會將您的答案標記爲已接受,因爲它可能是更普遍適用和有用的解決方案。我實際上最終做的是完全覆蓋ZipClassLoader類文件,直接加載(不打開JAR)插件類 - 我自己也必須放置在WAR中。醜陋的黑客,但它的工作... – OverclockedTim