You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现javax.servlet.Filter致Tomcat启动失败,改用GenericFilterBean问题排查

实现javax.servlet.Filter导致Tomcat启动失败的原因分析

问题背景

为给每个请求记录requestId并注入log4j2 MDC,编写了实现javax.servlet.Filter的RequestFilter类,将应用打包为WAR部署到Tomcat 8.5时启动失败;改为继承Spring提供的GenericFilterBean后,应用可正常启动。使用Spring Boot 2.7.5,需分析问题根源。

原Filter代码

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID; // 原代码缺失该导入,会导致编译错误

import org.apache.log4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.util.ContentCachingResponseWrapper;

@Component
public class RequestFilter implements Filter
{
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
  {
    try
    {
      String uuid = UUID.randomUUID().toString();
      MDC.put("requestId", uuid);
      HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
      ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(
          httpServletResponse
      );
      filterChain.doFilter(servletRequest, responseWrapper);
      responseWrapper.setHeader("requestId", uuid);
      responseWrapper.copyBodyToResponse();
    }
    finally
    {
      MDC.clear();
    }
  }
}

启动报错日志

4-Nov-2022 10:34:33.941 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal One or more Filters failed to start. Full details will be found in the appropriate container log file
04-Nov-2022 10:34:33.941 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal Context [/Application] startup failed due to previous errors
PreDestroy invoked...
04-Nov-2022 10:34:34.005 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [Application] created a ThreadLocal with key of type [com.abc.Class$1] (value [com.abc.Class$1@6a28aaa3]) and a value of type [com.abc.String] (value ["Random"]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
04-Nov-2022 10:34:34.005 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [Application] created a ThreadLocal with key of type [com.abc.Class$1] (value [com.abc.Class$1@6f649cfe]) and a value of type [com.abc.String] (value ["RANDOM"]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
04-Nov-2022 10:34:34.005 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [Application] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@36663d16]) and a value of type [io.netty.util.internal.InternalThreadLocalMap] (value [io.netty.util.internal.InternalThreadLocalMap@19b4f4cb]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
[2022-11-04 10:34:34,014] Artifact Application.war: Error during artifact deployment. See server log for details.
04-Nov-2022 10:34:34.782 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/apache-tomcat-8.5.58/webapps/manager]
04-Nov-2022 10:34:34.816 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/apache-tomcat-8.5.58/webapps/manager] has finished in [35] ms
04-Nov-2022 10:34:37.547 INFO [mysql-cj-abandoned-connection-cleanup] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load []. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
  java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load []. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1378)
    at org.apache.catalina.loader.WebappClassLoaderBase.getResource(WebappClassLoaderBase.java:1030)
    at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.checkThreadContextClassLoader(AbandonedConnectionCleanupThread.java:117)
    at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:84)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:750)

问题根源分析

1. 直接实现javax.servlet.Filter的容器适配缺陷

虽然Servlet 3.0+为Filter接口的init()和destroy()提供了默认实现,但Spring Boot将Filter Bean整合到Tomcat容器时,直接实现Filter的类无法利用Spring的容器适配能力:

  • Spring的GenericFilterBean作为Filter的抽象实现,已封装了FilterConfig绑定、Spring环境变量注入、Bean生命周期管理等逻辑;
  • 直接实现Filter的类即使加了@Component注解,Spring在将其注册到Tomcat时,无法正确传递容器配置,导致过滤器初始化失败,触发Tomcat的One or more Filters failed to start核心错误。

2. 代码的隐性问题

原代码还存在两个易忽略的问题:

  • 缺失java.util.UUID的导入语句,会导致编译错误;
  • 使用的org.apache.log4j.MDC是log4j 1.x的API,与你提到的log4j2不匹配,应替换为org.apache.logging.log4j.ThreadContext(log4j2的MDC实现),否则可能引发依赖冲突,间接影响启动。

3. 内存泄漏等报错是连带问题

日志中的ThreadLocal内存泄漏、MySQL连接清理线程错误,不是启动失败的根本原因,而是应用启动失败后,容器停止过程中未正确清理资源导致的次生问题:

  • 应用启动失败被强制停止,Netty、MySQL连接池等线程的ThreadLocal变量未及时清理;
  • MySQL的AbandonedConnectionCleanupThread在应用停止后仍尝试加载资源,触发类加载器非法访问错误。

继承GenericFilterBean解决问题的原因

GenericFilterBean是Spring为Servlet过滤器提供的适配类,核心作用包括:

  • 实现Filter接口的同时,集成BeanNameAware、EnvironmentAware等Spring接口,自动获取上下文配置;
  • 重写init(FilterConfig filterConfig)方法,将容器配置参数绑定到Bean属性,完成过滤器与Spring容器的无缝整合;
  • 规范过滤器的生命周期管理,确保在Tomcat中能正确启动和销毁。

内容的提问来源于stack exchange,提问作者Darshit Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:12:06