Spring Boot替换Tomcat为Jetty引入JSP时JarScanner启动报错
问题根因
提示ServletContext中未配置org.apache.tomcat.JarScanner只是表象,不是问题触发的核心原因。引入org.eclipse.jetty:apache-jsp依赖后,JSP初始化阶段会触发Apache TLD标签库扫描逻辑,默认全量扫描classpath下所有jar包内的标签描述符、web片段资源。Firebase Java Admin SDK依赖的grpc系列jar(尤其是grpc-netty-shaded这类带嵌套类路径结构的shaded包),在Tomcat JarScanner默认实现下的路径解析规则和Jetty类加载结构不兼容,扫描时会将嵌套jar的虚拟路径识别为本地文件系统路径读取,最终抛出FileNotFoundException,手动补Tomcat JarScanner配置无法解决该问题,本质是类加载逻辑不匹配导致的扫描路径错误。
可行排查与解决方向
- 方案1:优先推荐 配置Jetty JSP扫描的排除规则,跳过非必要jar的TLD扫描
直接在Spring Boot配置类中注册JettyServerCustomizer,缩小JSP扫描的jar范围,只扫描JSP、JSTL相关的依赖包,从根源避免扫描到grpc、Firebase相关的结构特殊jar,示例配置:
该方案不需要调整依赖,改动量最小,绝大多数同类场景可直接生效。@Bean public JettyServerCustomizer jettyJspScanExcludeCustomizer() { return server -> { for (Handler handler : server.getHandlers()) { if (handler instanceof ServletContextHandler contextHandler) { // 仅匹配JSP、JSTL相关jar参与TLD扫描 contextHandler.setAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/[^/]*jstl.*\\.jar$|.*/[^/]*jetty.*jsp.*\\.jar$" ); } } }; } - 方案2:替换默认JarScanner实现,适配Jetty类加载逻辑
不要硬注入Tomcat原生的JarScanner实现,在ServletContext初始化阶段注册适配Jetty路径规则的JarScanner,替换apache-jsp默认加载的Tomcat实现:
自定义的@Bean public ServletContextInitializer scannerOverrideInitializer() { return servletContext -> { servletContext.setAttribute( "org.apache.tomcat.JarScanner", new JettyCompatJarScanner() ); }; }JettyCompatJarScanner实现JarScanner接口即可,重写扫描逻辑时遇到嵌套类路径资源直接跳过,或调用Jetty原生JarResource做路径解析,不要直接通过File类读取路径。 - 方案3:排除apache-jsp中携带的Tomcat冲突依赖
org.eclipse.jetty:apache-jsp会传递引入Tomcat embed核心包,这些类和Jetty容器本身的类加载逻辑存在冲突,引入时直接排除该传递依赖:
如果项目仅使用基础JSTL标签,也可以将apache-jsp替换为和当前Jetty版本完全匹配的<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>apache-jsp</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> </exclusion> </exclusions> </dependency>org.eclipse.jetty:jetty-jsp,从根源避免引入Tomcat原生扫描逻辑。 - 方案4:调整Firebase Admin SDK依赖引入方式,替换shaded grpc包
排除Firebase Admin SDK依赖的grpc-netty-shaded包,改用标准结构的非shadedgrpc-netty依赖,这类jar是标准单文件结构,不会触发JarScanner的路径解析错误:<dependency> <groupId>com.google.firebase</groupId> <artifactId>firebase-admin</artifactId> <exclusions> <exclusion> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> <version>与Firebase依赖的grpc核心包版本保持一致</version> </dependency>
排查注意:不要尝试手动声明Tomcat JarScanner的Bean注入Spring上下文,这类Tomcat原生组件在Jetty容器下无法被ServletContext正确识别,反而会加重类加载冲突。优先按方案1到方案4的顺序测试,90%以上的场景在方案1阶段即可解决问题。
内容的提问来源于stack exchange,提问作者Mark Su
相关产品推荐
相关产品推荐

