2016-09-15 57 views
1

每當我在tomcat中部署一個web應用程序時,WEB-INF/lib中的所有jar都將被加載到應用程序ClassLoader中。如何將動態從不同位置加載到當前類加載器?

我有幾個其他位置的一些jar集,像WEB-INF/ChildApp1/*。jar,WEB-INF/ChildApp2/*。jar ..... 根據用戶請求我想將一些jar集加載到當前的類加載器中。

注意:我不想創建任何子類加載器。

我的真實需求是,以編程方式如何將jar添加到當前類加載器中。

+0

我的要求是,從不同的位置罐裝載到當前類loader.Don't要創建子類加載器。 –

+0

你想實現的是不是標準的,如果沒有自定義類加載器,你將無法做到這一點 –

+0

非標準位置的列表將在運行時改變,或者它是固定的? –

回答

1

我做了一次,但這是有點破解。請參閱下面的代碼:

final URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 
     final Class<URLClassLoader> sysclass = URLClassLoader.class; 
     // TODO some kind of a hack. Need to invent better solution. 
     final Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class }); 
     method.setAccessible(true); 
     for (final File jar : jars) { 
      method.invoke(sysloader, new URL[] { jar.toURI().toURL() }); 
     } 

你需要ClassLoader.getSystemClassloader()更改爲您要使用的類加載器。您也可以檢查,如果這是一個URLClassLoader 我認爲有更好的解決方案的實例,但是這個工作對我來說

+0

看來你是在系統類loader中添加jar。如果有任何jar版本衝突,那麼我的應用程序可能無法按預期工作。例如,jsonv1.jar已經加載到webapp CL(Application class loader)中,那麼如果我加載jsonv2.jar在系統CL,如你所建議的。然後我的應用程序可能會導致NoSuchMethodError或NoClassDefFoundError? –

+0

是的,我使用了系統類加載器(我有一個非常簡單的例子,並確保一些jar只能加載一次),但對於我,我建議使用不同的加載器。我不是100%確定會發生什麼,如果你添加另一個包含相同類的jar。我會說它會繼續使用舊的。但是這只是我的猜測,但很容易檢查 – Sergi

1

您需要實現自己的WebappClassLoaderBaseloadercontext.xml配置進行定義。

實現你WebappClassLoaderBase

最簡單的方法是延長WebappClassLoader爲未來

package my.package; 

public class MyWebappClassLoader extends WebappClassLoader { 

    public MyWebappClassLoader() { 
    } 

    public MyWebappClassLoader(final ClassLoader parent) { 
     super(parent); 
    } 

    @Override 
    public void start() throws LifecycleException { 
     String[] paths = {"/WEB-INF/ChildApp1/lib", "/WEB-INF/ChildApp2/lib"}; 
     // Iterate over all the non standard locations 
     for (String path : paths) { 
      // Get all the resources in the current location 
      WebResource[] jars = resources.listResources(path); 
      for (WebResource jar : jars) { 
       // Check if the resource is a jar file 
       if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { 
        // Add the jar file to the list of URL defined in the parent class 
        addURL(jar.getURL()); 
       } 
      } 
     } 
     // Call start on the parent class 
     super.start(); 
    } 
} 

部署您WebappClassLoaderBase

  • 使用對應到Tomcat版本tomcat的罐子構建自己的WebappClassLoaderBase那可從here獲得。
  • 從中
  • 創建一個罐子,把罐子在的tomcat/lib目錄,以使其可從Common ClassLoader

配置您WebappClassLoaderBase

定義WebappClassLoaderBasecontext.xml

<Context> 
    ... 
    <Loader loaderClass="my.package.MyWebappClassLoader" /> 
</Context> 

完成後,現在您的web應用程序將能夠加載來自/WEB-INF/ChildApp1/lib/WEB-INF/ChildApp2/lib的jar文件。


響應更新

正如你所喜歡做同樣的事情,但只有一個war,你將需要使用一個黑客動態添加您的jar文件。

這裏是你如何進行:

實現一個ServletContextListener添加您的jar文件

爲了動態添加jar文件時上下文被初始化,您需要創建一個ServletContextListener那將通過反射來呼叫URLClassLoader#addURL(URL),這是一個醜陋的黑客,但它的工作。請注意,它的工作原理是因爲Tomcat中的webapp的ClassLoaderWebappClassLoader,它實際上是URLClassLoader的子類。

package my.package; 

public class MyServletContextListener implements ServletContextListener { 

    @Override 
    public void contextInitialized(final ServletContextEvent sce) { 
     try { 
      // Get the method URLClassLoader#addURL(URL) 
      Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); 
      // Make it accessible as the method is protected 
      method.setAccessible(true); 
      String[] paths = {"/WEB-INF/ChildApp1/lib", "/WEB-INF/ChildApp2/lib"}; 
      for (String path : paths) { 
       File parent = new File(sce.getServletContext().getRealPath(path)); 
       File[] jars = parent.listFiles(
        new FilenameFilter() { 
         @Override 
         public boolean accept(final File dir, final String name) { 
          return name.endsWith(".jar"); 
         } 
        } 
       ); 
       if (jars == null) 
        continue; 
       for (File jar : jars) { 
        // Add the URL to the context CL which is a URLClassLoader 
        // in case of Tomcat 
        method.invoke(
         sce.getServletContext().getClassLoader(), 
         jar.toURI().toURL() 
        ); 
       } 
      } 

     } catch (Exception e) { 
      throw new IllegalStateException(e); 
     } 
    } 

    @Override 
    public void contextDestroyed(final ServletContextEvent sce) { 
    } 
} 

聲明你ServletContextListener

在你的web應用的web.xml只需添加:

<listener> 
    <listener-class>my.package.MyServletContextListener</listener-class> 
</listener> 
+0

謝謝,我會測試並讓你知道。第一個問題,爲什麼要在tomcat中保存WebappClassLoaderBase共享庫(tomcat/lib),爲什麼tomcat不能創建WebappClassLoaderBase CL應用程序類加載器)從我的WEB-INF/lib ....第二個,WebappClassLoaderBase CL將有WEB-INF/lib +/WEB-INF/ChildApp1/lib「+」/ WEB-INF/ChildApp2/lib「罐子? –

+1

#1因爲加載程序是全局的,並不是特定於給定的webapp,所以它可以在通用類加載器中定義,以便在tomcat啓動時可用#2是 –

+0

感謝Nicolas,它的工作。所有外部位置和WEB-INF/lib jar我的意思是在after()之後,我的意思是在after()之後部署戰爭,在應用程序啓動時,是否可以加載所有罐子在單個CL? –