java - 扫描嵌入式 Tomcat 中的 list 类路径 jar

标签 java spring-boot servlet-3.0 embedded-tomcat-8

我有一个嵌入式 Tomcat 应用程序,打包为可执行(瘦)jar,具有多个外部 jar 依赖项。

构建过程生成一个 META-INF/MANIFEST.MF ,其中包含头字段 Main-ClassClass-Path (带有一个条目每个运行时依赖项)。

我想使用简单的 java -jar my_app.jar 执行应用程序,但我无法让 Tomcat 扫描这些依赖的 jar(以便发现 TLD)或 @HandlesTypes 类(如 Spring WebApplicationInitializer)。

我以这种方式配置 jar 扫描:

StandardJarScanner jarScanner = (StandardJarScanner) ctx.getJarScanner();
jarScanner.setScanBootstrapClassPath(true);
jarScanner.setScanClassPath(true);

所有的 jar 都有一个 META-INF 文件夹,但扫描仪完全忽略它们。

有什么想法吗?

Note: I can make this work using different approaches (fat jar, running from maven, ...) but I am interested in making it work this way, as any other java application.

最佳答案

Tomcat 通过重复调用 URLClassLoader.getURLS() 来获取要扫描的 jar URL在类加载器层次结构中(自下而上)

URLClassLoader.getURLS() 起系统类加载器出现问题当 java 应用程序执行为 java -jar <executable-jar> 时,不返回类路径 jar

参见:How does a classloader load classes reference in the manifest classpath?

在上一篇文章中,建议使用反射来访问系统类加载器实例中的私有(private)字段,但这会带来几个问题:

  • 安全管理员可以禁止此访问
  • 解决方案取决于实现

所以我想出了另一种方法:

  1. 对于给定的类加载器,枚举所有可用的 list cl.getResources("META-INF/MANIFEST.MF") 。这些 list 可以是由当前类加载器或其后代类加载器管理的 jar。
  2. 对其父类加载器执行相同的操作
  3. 返回在 (1) 中但不在 (2) 中 list 的 jar 集

此方法工作的唯一要求是类路径中的 jar 必须具有 list 才能返回(没什么要求)。

/**
 * Returns the search path of URLs for loading classes and resources for the 
 * specified class loader, including those referenced in the 
 * {@code Class-path} header of the manifest of a executable jar, in the 
 * case of class loader being the system class loader. 
 * <p>
 * Note: These last jars are not returned by 
 * {@link java.net.URLClassLoader#getURLs()}.
 * </p>
 * @param cl
 * @return 
 */
public static URL[] getURLs(URLClassLoader cl) {
    if (cl.getParent() == null || !(cl.getParent() 
            instanceof URLClassLoader)) {
        return cl.getURLs();
    }
    Set<URL> urlSet = new LinkedHashSet();
    URL[] urLs = cl.getURLs();
    URL[] urlsFromManifest = getJarUrlsFromManifests(cl);
    URLClassLoader parentCl = (URLClassLoader) cl.getParent();
    URL[] ancestorUrls = getJarUrlsFromManifests(parentCl);

    for (int i = 0; i < urlsFromManifest.length; i++) {
        urlSet.add(urlsFromManifest[i]);
    }
    for (int i = 0; i < ancestorUrls.length; i++) {
        urlSet.remove(ancestorUrls[i]);
    }
    for (int i = 0; i < urLs.length; i++) {
        urlSet.add(urLs[i]);
    }
    return urlSet.toArray(new URL[urlSet.size()]);
}

/**
 * Returns the URLs of those jar managed by this classloader (or its 
 * ascendant classloaders) that have a manifest
 * @param cl
 * @return 
 */
private static URL[] getJarUrlsFromManifests(ClassLoader cl) {
    try {
        Set<URL> urlSet = new LinkedHashSet();
        Enumeration<URL> manifestUrls = 
                cl.getResources("META-INF/MANIFEST.MF");
        while (manifestUrls.hasMoreElements()) {
            try {
                URL manifestUrl = manifestUrls.nextElement();
                if(manifestUrl.getProtocol().equals("jar")) {
                    urlSet.add(new URL(manifestUrl.getFile().substring(0, 
                            manifestUrl.getFile().lastIndexOf("!"))));
                }
            } catch (MalformedURLException ex) {
                throw new AssertionError();
            }
        }
        return urlSet.toArray(new URL[urlSet.size()]);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

Tomcat注册问题:https://bz.apache.org/bugzilla/show_bug.cgi?id=59226

关于java - 扫描嵌入式 Tomcat 中的 list 类路径 jar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35922072/

相关文章:

Java/JavaFX : Set Swing Icon for JavaFX label

java - 在 Mac OS X 上使用 java 命令运行 DynamoDB Local

java - JUnit 测试 MongoDB SpringBoot

javascript - 如何使用事件更新 Thymeleaf

java - 无法使用 servlet 3 可插入性功能将 Jersey 2.16 集成到 Tomcat 8.0.12

java - 休息 API : Using @QueryParam to load data from MongoDB

java - 如果需要支持两个版本,如何处理 Java 中的包名称更改?

spring - 请使用 'MongoMappingContext#setAutoIndexCreation(boolean)' 或覆盖 'MongoConfigurationSupport#autoIndexCreation()' 以明确

asynchronous - 如何为通过 Spring DelegatingFilterProxy 链调用的过滤器设置异步支持标志?

java - 使用 servlet 3.0 的 JBoss 6 session 安全特性的问题