如何从不同的位置动态加载jar到当前的类加载器?

时间:2016-09-15 10:20:31

标签: java tomcat jar classloader dynamic-class-loaders

每当我在tomcat中部署Web应用程序时,WEB-INF / lib中的所有jar都将被加载到应用程序ClassLoader中。

我的其他几个地方都有一些罐子,比如WEB-INF / ChildApp1 / * .jar,WEB-INF / ChildApp2 / * .jar ..... 根据用户请求,我想将一些jar加载到当前的类加载器中。

注意:我不想创建任何子类加载器。

我真正的要求是,以编程方式将jar添加到当前的类加载器中。

2 个答案:

答案 0 :(得分:3)

我曾经做过一次,但这有点像黑客。请参阅以下代码:

    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的实例 我认为有更好的解决方案,但这对我有用

答案 1 :(得分:2)

您需要在context.xml的{​​{3}}配置中实施自己的WebappClassLoaderBase

实施WebappClassLoaderBase

最简单的方法是将loader扩展为下一个

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

  • 使用与WebappClassLoader提供的tomcat版本对应的tomcat jar构建您自己的WebappClassLoaderBase
  • 从中创建一个jar
  • 将jar放入 tomcat / lib ,以便从here
  • 中获取

配置WebappClassLoaderBase

WebappClassLoaderBase

中定义context.xml
<Context>
    ...
    <Loader loaderClass="my.package.MyWebappClassLoader" />
</Context>

完成后,现在您的webapps将能够从/WEB-INF/ChildApp1/lib/WEB-INF/ChildApp2/lib加载jar文件。

响应更新

如果你想做同样的事情但只有war,你需要使用黑客来动态添加你的jar文件。

以下是您可以继续操作的方法:

实施ServletContextListener添加jar文件

为了在初始化上下文时动态添加jar文件,您需要创建一个ServletContextListener,它将通过反复调用Common ClassLoader,这是一个丑陋的黑客但它可以正常工作。请注意,它的工作原理是因为Tomcat中的Web应用程序的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.xml中,只需添加:

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