2016-09-15 83 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? –

相关问题