从 Spring 启动 1.2 升级到 1.5.2 后,Tomcat 8.5 启动期间出现 FileNotFoundException

AFTER upgrade from Spring boot 1.2 to 1.5.2, FileNotFoundException during Tomcat 8.5 Startup

升级Spring从1.2.0启动1.5.2.

升级后,Tomcat 8.5FileNotFoundException 启动.

下面是其中一个异常,它抛出了超过~10个类似的异常。

我不知道这些罐子的 用途,换句话说,我没有为 中的这些罐子添加 <dependency>pom.xml.

INFO: Starting Servlet Engine: Apache Tomcat/8.5.11
Apr 06, 2017 3:53:57 PM org.apache.tomcat.util.scan.StandardJarScanner scan
WARNING: Failed to scan [file:/C:/Users/myname/.m2/repository/com/sun/xml/ws/jaxws-rt/2.1.7/jaxws-api.jar] from classloader hierarchy
java.io.FileNotFoundException: C:\Users\myname\.m2\repository\com\sun\xml\ws\jaxws-rt.1.7\jaxws-api.jar (The system cannot find the file specified)
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.<init>(ZipFile.java:219)
    at java.util.zip.ZipFile.<init>(ZipFile.java:149)
    at java.util.jar.JarFile.<init>(JarFile.java:166)
    at java.util.jar.JarFile.<init>(JarFile.java:130)
    at org.apache.tomcat.util.scan.JarFileUrlJar.<init>(JarFileUrlJar.java:60)
    at org.apache.tomcat.util.scan.JarFactory.newInstance(JarFactory.java:48)
    at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:338)
    at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:288)
    at org.apache.jasper.servlet.TldScanner.scanJars(TldScanner.java:262)
    at org.apache.jasper.servlet.TldScanner.scan(TldScanner.java:104)
    at org.apache.jasper.servlet.JasperInitializer.onStartup(JasperInitializer.java:101)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5178)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

如有任何帮助,我们将不胜感激。

根本原因:

根据 Tomcat Wiki,Servlet 3.0 规范要求在服务器启动期间进行 Jar 扫描。

Tomcat 正在使用 org.apache.tomcat.util.scan。StandardJarScanner 用于此目的。

来自 StandardJarScanner 的 javadoc。

The default JarScanner implementation scans the WEB-INF/lib directory followed by the provided classloader and then works up the classloader hierarchy. This implementation is sufficient to meet the requirements of the Servlet 3.0 specification as well as to provide a number of Tomcat specific extensions. The extensions are:

  • Scanning the classloader hierarchy (enabled by default) Testing all files to see if they are JARs (disabled by default)

  • Testing all directories to see if they are exploded JARs (disabled by default)

  • All of the extensions may be controlled via configuration.

解决方案 1:Spring 引导特定。

我们可以禁用这个jar扫描。

我通过在 application-xxx.properties 文件中添加以下 属性 来禁用它。这个 属性 是 Spring Boot specific.

# Comma-separated list of additional patterns that match jars to ignore for TLD scanning.    
server.tomcat.additional-tld-skip-patterns=*.jar

您可以从 Tomcat here.

中找到 相似的属性

这些属性可用于配置传统tomcat(-spring引导)应用程序。

解决方案2:Spring具体

您可以为 manifest 文件禁用 JarScanner,如下所示。

@Bean
public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
  return new TomcatEmbeddedServletContainerFactory() {
    @Override
    protected void postProcessContext(Context context) {
      ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
    }
  };
}

解决方案3:传统独立Tomcat:

<Context>
  ...
  <JarScanner scanManifest="false"/>
  ...
</Context>

参考:The Jar Scanner Component.

只是为了改进 Sundaraj 的发现...完全禁用 TLD 扫描会中断 JSP/JSTL 支持。

问题是classpath本身没问题,只有Tomcat 另外扫描每个Jar的manifest文件,由于使用 Maven,每个 Jar 都在其自己的目录中,这会生成无意义的路径(可能 运行 来自 Eclipse?)。

因此,如果您想继续使用 JSP 和 JSTL,您应该只禁用清单扫描。

对于 Spring Boot 2.0,将其添加到您的应用程序配置中:

  @Bean
  public TomcatServletWebServerFactory tomcatFactory() {
    return new TomcatServletWebServerFactory() {
      @Override
      protected void postProcessContext(Context context) {
        ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
      }
    };
  }

JarScannerFactory loaded StandardJarScanner which we need to configure here,所以这个(对于 spring boot 2.1.8)也有效。

import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebContextConfiguration {

    @Bean
    public ServletContextInitializer servletContextInitializer() {
        return context -> context.setAttribute(
                JarScanner.class.getName(),
                new StandardJarScanner() {{
                    setScanManifest(false);
                }}
        );
    }
}

对我来说,它在我添加到

后工作

tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\

*.jar

catalina.properties.