当前位置: 首页 > 知识库问答 >
问题:

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

闻安宜
2023-03-14

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

我很少有其他位置有一些jars集,例如WEB-INF/ChildApp1/*. jar、WEB-INF/ChildApp2/*. jar......根据用户请求,我想将一些jars集加载到当前的类加载器中。

注意:我不想创建任何子类装入器。

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

共有2个答案

仲柏
2023-03-14

您需要在< code>context.xml中的< code>loader配置中实现您自己的< code > 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();
    }
}
    < li >使用与您的tomcat版本相对应的tomcat jar构建您自己的< code > WebappClassLoaderBase ,此处可获得。 < li >从它创建一个jar < li >并将jar放在tomcat/lib中,以便可以从< code > Common class loader 中使用它

context.xml中定义您的WebappClassLoaderBase

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

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

由于您想做同样的事情,但只有战争,因此您需要使用黑客来动态添加jar文件。

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

为了在初始化上下文时动态添加您的jar文件,您需要创建一个ServletContextListener,它将通过反射调用URLClassLoader#addURL(URL),这是一个丑陋的黑客,但它有效。请注意,它之所以有效,是因为Tomcat中webapp的ClassLoader是一个WebappClassLoader,它实际上是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) {
    }
}

Web 中.xml只需添加以下内容:

<listener>
    <listener-class>my.package.MyServletContextListener</listener-class>
</listener>
洪河
2023-03-14

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

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

 类似资料:
  • 问题内容: 我正在使用hibernate创建一个jar。我曾经遇到过一个情况我需要更改设置(URL)的时候,所以我想加载这样 但是然后运行该项目,我得到这个异常 如何从课程路径以外的其他位置加载? 问题答案: 上课有方法 请尝试以下操作,它应该可以正常工作:) 不同之处在于您使用了一种方法,该方法期望类路径中的资源,但是所期望的却在其中,因此您可以传递它。

  • 问题内容: 我正在尝试在Android平台上的运行时动态加载类。该类包含在单独的库JAR文件中,但与APK打包在一起(按照SDK中的新库机制)。使用Class.forname方法时,我收到未找到的类异常。我已经看到了有关DexClassLoader方法的一些讨论,但是我找不到如何使用它的好例子(以及是否是最好的使用方法- 它似乎比forname方法复杂得多!)。 如果有人可以提供示例代码片段来说明

  • 问题内容: 我尝试将jar动态加载到Java项目中。 这是类加载器的代码: 以及Jar中包含的Class: } 它给了我: 您对此有什么想法吗?谢谢。 问题答案: 您的文件网址似乎无效。 “ Windows中的文件URI”说 对于本地Windows文件路径 Windows中相应的有效文件URI为: 这表明冒号后需要 三个斜杠 ,但是您要在其中计算的URL 在。之后仅有 两个斜线。也许 应该 或者你

  • 我在部署一个使用部署系统注入的SPI实现的Quarkus应用程序时遇到了一个问题。 感兴趣的事情: null

  • 问题内容: 我整天都在这个问题上。我的问题是如何在实例上进行MethodHandle.invokeExact调用,该实例的类类型在程序运行时动态加载。为了使问题更清楚,我在下面显示我的示例代码: 在此示例中,expClass是动态加载的,其类类型为。下一行的obj实例被声明为BaseTemplate,其真实类型为。类AddSample是BaseTemplate的子类。然后,向其添加函数创建了一个M

  • 我正在使用Angular UI路由器,希望重新加载当前状态并刷新所有数据/重新运行当前状态及其父级的控制器。 我有3个州级:directory.organisations.details 目录组织包含一个包含组织列表的表。单击表loads目录中的项目。组织。$StateParams传递项目ID的详细信息。因此,在详细信息状态下,我加载此项目的详细信息,编辑它们,然后保存数据。目前一切正常。 现在我