今天帮同事查了个参数丢失的问题,确认是由参数大小超过 Tomcat 默认限制引起,源码位于 tomcat/Request.java:
1 | if ((maxPostSize >= 0) && (len > maxPostSize)) { |
业务层的感知就是参数莫名丢失了,查询了下 Tomcat 为何仅设置了参数解析失败的标志而为何未抛出相关异常。其实 Tomcat 是提供了过滤器 FailedRequestFilter
向客户端返回相关异常信息的,源码位于 tomcat/FailedRequestFilter.java at 8.5.82:
1 | /** |
但是该过滤器默认情况下并未启用。根据注释可以知道,该过滤器可能消费 HTTP 请求体,所以使用了 request.getInputStream()
或 request.getReader()
的 Servlet 需要小心使用该过滤器。其中该过滤器源码中的 log
引起了我的注意,特别是该段注释:
Log must be non-static as loggers are created per class-loader and this Filter may be used in multiple class loaders
关于 log
的提交位于 Logs for Filters must be non-static as loggers are created per class-… · apache/tomcat@dd44360 · GitHub,在该次提交的 changelog 中也只是提到是为了处理 reload 这种场景:
Make all loggers associated with Tomcat provided Filters non-static to ensure that log messages are not lost when a web application is reloaded.
经过询问后明确 log
未使用 static
修饰的原因如下:
When Webapp classloader loads the Filter class, it does not “load the class” (i.e. does not read the bytes from a jar archive and does not produce a Class out of those bytes). It delegates the task to its parent in the classloader hierarchy. Thus, the Filter class is loaded by the Common classloader.
At the same time, the logging configuration for each web application may be different:
A web application may provide its own copy of logging.properties file by placing it into its own WEB-INF/classes/ directory.
即 FailedRequestFilter
是 Tomcat 内部的类,经过 Webapp classLoader 的委托后实际是由 Common classLoader 进行加载,所以只会被加载一次,如果 log
被 static 修饰了,那么如果 webapp 中的日志配置变更后,因为 log
实例未被重建则会导致最新的日志配置不能生效。所以将其声明为非静态是为了保证每次实例化 FailedRequestFilter
时也重新实例化 log
实例以保证 webapp 中的最新日志配置生效。
Reference
Classloader Hierarchy for Tomcat
Question about the log variable in Filters