实现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
相关产品推荐
相关产品推荐

