Tomcat 的 Valve(阀门)是什么?它和 Filter 有什么区别?
简化版
**Valve(阀门)是 Tomcat 特有的「请求处理拦截机制」——它像一道道「阀门」串在 Tomcat 的请求处理管道(Pipeline)上,请求进来要依次流过这些 Valve,每个 Valve 可以对请求做处理(如记录访问日志、IP 过滤、单点登录)。**它和 Filter 的核心区别在「所属层次」:Valve 是 Tomcat 容器级别的(属于 Tomcat 自己的架构,作用于 Tomcat 的组件层面——Engine/Host/Context 每一层都有自己的 Pipeline 和 Valve),在 Servlet 规范之外、请求到达 Servlet/Filter 之前就执行;Filter 是 Servlet 规范级别的(属于 Servlet 标准,作用于 Web 应用内、Servlet 之前),任何 Servlet 容器都支持(可移植)。所以:Valve 是 Tomcat 私有的、更底层(容器级、跨应用),适合做「Tomcat 层面的通用处理」(访问日志 AccessLogValve、错误页 ErrorReportValve、远程 IP RemoteIpValve);Filter 是标准的、应用级,适合做「Web 应用内的处理」(认证、编码、跨域)。记忆:Valve 是 Tomcat 私有的容器级拦截器(不可移植),Filter 是 Servlet 标准的应用级拦截器(可移植);请求顺序上 Valve 在 Filter 之前。
详细版
Valve vs Filter:
| 维度 | Valve(阀门) | Filter(过滤器) |
|---|---|---|
| 所属 | Tomcat 私有(容器级) | Servlet 规范(应用级) |
| 可移植 | 否(只 Tomcat 有) | 是(任何容器) |
| 层次 | Tomcat 组件(Engine/Host/Context) | Web 应用内 |
| 执行时机 | 更早(请求到 Servlet 之前,Filter 之前) | Servlet 之前(Valve 之后) |
| 配置 | server.xml / context.xml | web.xml / @WebFilter |
| 典型 | AccessLogValve、RemoteIpValve | 认证、编码、CORS |
Tomcat 的请求处理层次(每层有 Pipeline + Valve):
Engine(Pipeline: StandardEngineValve...)
→ Host(Pipeline: StandardHostValve、AccessLogValve、ErrorReportValve...)
→ Context(Pipeline: StandardContextValve...)
→ Wrapper(Pipeline: StandardWrapperValve)→ Servlet
→ 到这里才进入 Servlet 规范:Filter → Servlet
请求流经顺序:
连接器 → Engine Valve → Host Valve → Context Valve → Wrapper Valve
→ (进入 Servlet 规范) Filter → Servlet
→ Valve 在 Filter 之前(更底层、更早)
<!-- 配置 Valve(在 server.xml / context.xml)-->
<Host name="localhost" ...>
<!-- 访问日志 Valve -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" prefix="access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<!-- IP 过滤 Valve -->
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="192\.168\..*" />
</Host>
⚠️ 理解 Valve 和 Filter 的区别,关键是「层次不同」——Valve 是 Tomcat 架构内部的,Filter 是 Servlet 规范内部的,两者在不同的层级。Valve 在 Tomcat 的容器组件层面(Engine/Host/Context/Wrapper 每一层都有自己的 Pipeline,Pipeline 上串着 Valve)——它是Tomcat 私有的、在 Servlet 规范之外,所以换个容器(Jetty/Undertow)就没有 Valve 了(不可移植);它执行得更早(请求进入 Servlet 应用、Filter 之前)、作用范围更大(可以是整个 Engine/Host 级别,跨多个应用)。Filter 在 Servlet 规范层面(Web 应用内、Servlet 之前)——它是标准的、任何 Servlet 容器都支持(可移植),作用于单个 Web 应用内。选择原则:需要「跨应用的、Tomcat 层面的通用处理」(如整个 Tomcat 的访问日志、IP 白名单、真实 IP 解析)→ 用 Valve;需要「应用内的、可移植的处理」(认证、编码、业务逻辑相关)→ 用 Filter。大多数业务开发用 Filter(可移植、标准),Valve 主要是 Tomcat 运维/底层配置时用。
完整版教学
一、Valve 是什么:Tomcat 的管道阀门
先理解 Valve 在 Tomcat 架构里的定位:
Tomcat 的组件层次(容器嵌套):
Server → Service → Engine → Host → Context → Wrapper → Servlet
(Engine=引擎、Host=虚拟主机、Context=一个应用、Wrapper=一个 Servlet)
Pipeline(管道)+ Valve(阀门):
每个容器(Engine/Host/Context/Wrapper)都有一个 Pipeline(管道)
Pipeline 上串着多个 Valve(阀门)
→ 请求流过这个容器时,依次经过 Pipeline 上的 Valve
Valve(阀门)的含义:
像水管上的一道道阀门——请求(水)流过时,每个阀门做处理
每个 Valve 是一个处理环节(记日志、过滤、认证等)
责任链模式:
Pipeline 上的 Valve 组成责任链
请求依次经过每个 Valve → 最后到达容器的核心处理(Basic Valve)
→ 每个 Valve 处理完调用下一个(invokeNext)
每层都有 Pipeline + Valve:
Engine 的 Pipeline、Host 的 Pipeline、Context 的、Wrapper 的
→ 请求从 Engine → Host → Context → Wrapper,每层过 Pipeline 的 Valve
所以 Valve = Tomcat 请求处理管道(Pipeline)上的阀门(处理环节)
Valve 在 Tomcat 架构的定位——Tomcat 组件层次(Server→Service→Engine→Host→Context→Wrapper→Servlet)。每个容器都有一个 Pipeline(管道)、Pipeline 上串着多个 Valve(阀门)——请求流过容器时依次经过 Pipeline 上的 Valve。Valve 像水管上的阀门(请求流过时每个阀门做处理,记日志/过滤/认证)。责任链模式(Valve 组成责任链、依次经过、最后到 Basic Valve)。每层都有 Pipeline+Valve(请求从 Engine→Host→Context→Wrapper 每层过 Valve)。理解「Valve=Tomcat 请求管道 Pipeline 上的阀门;Tomcat 组件层次 Engine/Host/Context/Wrapper 每个有 Pipeline、Pipeline 串多个 Valve;责任链模式依次经过;每层都有 Pipeline+Valve」,就理解了 Valve 的定位。
二、Valve vs Filter:层次不同
核心区别是「Valve 和 Filter 在不同层次」:
Valve(Tomcat 容器级):
① 属于 Tomcat 自己的架构(Pipeline + Valve)
② 在 Servlet 规范之外(Tomcat 私有)
③ 作用于 Tomcat 的容器层面(Engine/Host/Context)
④ 执行更早(请求进入 Servlet 应用之前)
⑤ 可以跨应用(Engine/Host 级别的 Valve 作用于多个应用)
⑥ 不可移植(换 Jetty/Undertow 就没有了)
Filter(Servlet 规范级):
① 属于 Servlet 标准规范
② 作用于 Web 应用内(Servlet 之前)
③ 任何 Servlet 容器都支持(可移植)
④ 执行在 Servlet 之前(但在 Valve 之后)
⑤ 作用于单个 Web 应用
⑥ 可移植(标准 API)
关键区别(层次):
Valve:Tomcat 架构内部(容器级、更底层、Tomcat 私有)
Filter:Servlet 规范内部(应用级、标准、可移植)
请求经过的顺序:
连接器 → Tomcat 的 Valve(Engine→Host→Context→Wrapper)
→ (进入 Servlet 规范) Filter → Servlet
→ Valve 在 Filter 之前(更早、更底层)
所以 Valve 是 Tomcat 私有容器级、Filter 是标准应用级,层次不同
核心区别是「层次不同」:Valve(Tomcat 容器级)——属于 Tomcat 架构(Pipeline+Valve)、Servlet 规范之外(Tomcat 私有)、作用于容器层面、执行更早(进 Servlet 应用之前)、可跨应用、不可移植(换容器就没了);Filter(Servlet 规范级)——属于 Servlet 标准、作用于 Web 应用内、任何容器支持(可移植)、在 Servlet 之前(但 Valve 之后)、作用单个应用。请求顺序:连接器 → Tomcat 的 Valve → Filter → Servlet(Valve 在 Filter 之前)。理解「Valve vs Filter 层次不同:Valve(Tomcat 私有容器级/Servlet 规范外/执行更早/可跨应用/不可移植)、Filter(Servlet 标准应用级/可移植/在 Servlet 之前);顺序连接器→Valve→Filter→Servlet(Valve 在 Filter 之前)」,就掌握了核心区别。
三、常见的内置 Valve
Tomcat 有一些常用的内置 Valve:
Tomcat 常用内置 Valve:
① AccessLogValve(访问日志):
记录每个请求的访问日志(IP、时间、URL、状态码、响应大小)
→ Tomcat 层面的访问日志(不是应用的日志)
② RemoteIpValve(真实 IP):
处理代理/负载均衡后的真实客户端 IP
从 X-Forwarded-For 头解析真实 IP
→ 应用拿到的 request.getRemoteAddr() 是真实 IP(不是代理的)
③ RemoteAddrValve / RemoteHostValve(IP 过滤):
按 IP/主机名允许或拒绝访问(allow/deny 正则)
→ IP 白名单/黑名单
④ ErrorReportValve(错误页):
生成错误页面(如 404、500 的默认错误页)
→ 可自定义或关闭(生产环境常自定义/关闭默认错误页)
⑤ SingleSignOn(单点登录):
Host 级别的单点登录(同一 Host 下多个应用共享认证)
⑥ StuckThreadDetectionValve(卡死线程检测):
检测处理时间过长的请求(可能卡死)
配置位置:
server.xml(Engine/Host 级)或 context.xml(Context 级)
<Valve className="..." 参数.../>
所以内置 Valve 处理 Tomcat 层面的通用需求(日志、真实 IP、IP 过滤等)
Tomcat 常用内置 Valve:① AccessLogValve(访问日志,记录每个请求的 IP/时间/URL/状态码,Tomcat 层面日志)、② RemoteIpValve(真实 IP,从 X-Forwarded-For 解析代理后的真实客户端 IP)、③ RemoteAddrValve/RemoteHostValve(IP 过滤,allow/deny 白名单黑名单)、④ ErrorReportValve(错误页,生产常自定义/关闭)、⑤ SingleSignOn(Host 级单点登录)、⑥ StuckThreadDetectionValve(卡死线程检测)。配置在 server.xml(Engine/Host 级)或 context.xml(Context 级)。理解「内置 Valve:AccessLogValve(访问日志)/RemoteIpValve(真实 IP 解析 X-Forwarded-For)/RemoteAddrValve(IP 过滤白名单)/ErrorReportValve(错误页)/SingleSignOn(Host 级 SSO)/StuckThreadDetectionValve(卡死检测);配置 server.xml/context.xml」,就掌握了常见内置 Valve。
四、执行时机与顺序
理解 Valve 和 Filter 的执行顺序:
请求处理的完整顺序(从底层到应用):
1. 连接器(Connector)接收请求、解析 HTTP
2. Engine 的 Pipeline(Engine 级 Valve)
3. Host 的 Pipeline(Host 级 Valve:AccessLogValve、
ErrorReportValve、RemoteIpValve...)
4. Context 的 Pipeline(Context 级 Valve)
5. Wrapper 的 Pipeline(Wrapper 级 Valve,最后是 StandardWrapperValve)
6. ★ 进入 Servlet 规范:
Filter 链(web.xml/@WebFilter 配的 Filter)
→ Servlet(Controller)
关键顺序:
Valve(各级)→ Filter → Servlet
→ Valve 在最外层、最早执行(Tomcat 容器层)
→ Filter 在 Servlet 之前(应用层)
→ Valve 比 Filter 更早、更底层
响应时反向:
Servlet → Filter(返回)→ Valve(返回)→ 连接器 → 客户端
→ 责任链的返回路径
为什么 Valve 更早:
Valve 在 Tomcat 容器层(请求刚进 Tomcat 就处理)
Filter 在应用层(请求进入某个 Web 应用后)
→ Valve 处理"进入应用之前"的事(日志、真实 IP、IP 过滤)
→ Filter 处理"应用内"的事(认证、编码)
所以顺序:Valve(各级容器)→Filter→Servlet,Valve 更早更底层
请求处理的完整顺序:1. 连接器接收解析 → 2. Engine Valve → 3. Host Valve(AccessLogValve/RemoteIpValve 等)→ 4. Context Valve → 5. Wrapper Valve → 6. 进入 Servlet 规范:Filter 链 → Servlet。关键顺序:Valve(各级)→ Filter → Servlet(Valve 在最外层最早执行、Filter 在 Servlet 之前、Valve 比 Filter 更早更底层)。响应时反向(Servlet→Filter→Valve→客户端)。为什么 Valve 更早:Valve 在 Tomcat 容器层(刚进 Tomcat 就处理)、Filter 在应用层(进入某应用后)。理解「顺序:连接器→Engine Valve→Host Valve→Context Valve→Wrapper Valve→Filter→Servlet;Valve 各级→Filter→Servlet,Valve 最外层最早;Valve 处理进入应用前(日志/真实 IP)、Filter 处理应用内(认证/编码)」,就掌握了执行时机与顺序。
五、自定义 Valve
可以自定义 Valve(继承 ValveBase):
自定义 Valve(较少用,一般用内置的):
继承 org.apache.catalina.valves.ValveBase
重写 invoke(Request request, Response response)
public class MyValve extends ValveBase {
public void invoke(Request request, Response response) throws ... {
// 前置处理(记录、过滤等)
long start = System.currentTimeMillis();
getNext().invoke(request, response); // ★ 调用下一个 Valve(责任链)
// 后置处理
long cost = System.currentTimeMillis() - start;
log("耗时: " + cost);
}
}
配置(server.xml / context.xml):
<Valve className="com.example.MyValve" />
关键点:
getNext().invoke(...):调用管道里的下一个 Valve(责任链传递)
→ 不调用就中断(请求不往下走)
自定义 Valve vs 自定义 Filter:
自定义 Valve:Tomcat 私有、容器级、要配 server.xml、不可移植
自定义 Filter:标准、应用级、web.xml/@WebFilter、可移植
→ 大多数需求用 Filter(可移植、简单)
→ 除非确实要"Tomcat 容器级、跨应用"的处理,才用 Valve
所以自定义 Valve 继承 ValveBase、重写 invoke、调 getNext(责任链)
自定义 Valve(较少用):继承 ValveBase、重写 invoke(request, response)(前置处理 → getNext().invoke() 调用下一个 Valve(责任链传递,不调就中断) → 后置处理)。配置在 server.xml/context.xml。自定义 Valve vs Filter:Valve(Tomcat 私有、容器级、配 server.xml、不可移植)、Filter(标准、应用级、web.xml/@WebFilter、可移植)——大多数需求用 Filter(可移植简单),除非要 Tomcat 容器级跨应用处理才用 Valve。理解「自定义 Valve:继承 ValveBase 重写 invoke(前置→getNext().invoke 调下一个 Valve 责任链→后置)、配 server.xml;vs Filter:Valve 私有容器级不可移植、Filter 标准应用级可移植;大多数用 Filter 除非要容器级跨应用」,就掌握了自定义 Valve。
六、实践与总结
总结 Valve 和 Filter 的选择:
选择(Valve vs Filter):
用 Valve(Tomcat 容器级):
① 需要 Tomcat 层面的通用处理(跨应用)
如整个 Tomcat 的访问日志、IP 白名单
② 需要在"进入应用之前"处理(更早)
③ 用内置的 Valve(AccessLogValve、RemoteIpValve 等)
→ 主要是 Tomcat 运维/配置时用
用 Filter(Servlet 标准):
① 应用内的处理(认证、编码、CORS、业务相关)
② 需要可移植(换容器不受影响)
③ 大多数业务开发用它
→ 业务开发的首选
实践建议:
① 业务开发用 Filter(可移植、标准、简单)
② Tomcat 层面的通用配置用 Valve(用内置的)
如访问日志(AccessLogValve)、真实 IP(RemoteIpValve)
③ 别自定义 Valve(除非确实要容器级跨应用,一般 Filter 够)
④ RemoteIpValve 常用(代理/负载均衡后拿真实 IP)
核心总结:
Valve = Tomcat 私有的容器级拦截器(Pipeline 上的阀门,不可移植)
Filter = Servlet 标准的应用级拦截器(可移植)
顺序:Valve(各级容器)→ Filter → Servlet(Valve 更早更底层)
业务用 Filter、Tomcat 层面通用处理用 Valve(内置的)
Valve vs Filter 选择:用 Valve(Tomcat 容器级)——需要 Tomcat 层面通用处理(跨应用如访问日志/IP 白名单)、进入应用前处理、用内置 Valve(主要 Tomcat 运维配置用);用 Filter(Servlet 标准)——应用内处理(认证/编码/CORS)、需要可移植、大多数业务开发用它。实践:业务用 Filter、Tomcat 层面通用配置用内置 Valve(AccessLogValve/RemoteIpValve)、别自定义 Valve(一般 Filter 够)。理解「选择:Valve(容器级跨应用/进入应用前/内置 Valve/Tomcat 运维)、Filter(应用内/可移植/业务开发首选);实践业务用 Filter、Tomcat 通用配置用内置 Valve、别自定义 Valve」,就掌握了选择和实践。
记忆钩子:「Valve(阀门)=Tomcat 特有的请求处理拦截器,在 Pipeline(管道)上串成责任链,Tomcat 组件每层(Engine/Host/Context/Wrapper)都有 Pipeline+Valve;★Valve vs Filter 层次不同:Valve(Tomcat 私有容器级/Servlet 规范外/执行更早/可跨应用/不可移植换容器就没了)、Filter(Servlet 标准应用级/可移植任何容器支持/在 Servlet 之前);请求顺序:连接器→Tomcat Valve(各级)→Filter→Servlet(Valve 在 Filter 之前更底层);内置 Valve:AccessLogValve(访问日志)/RemoteIpValve(代理后真实 IP)/RemoteAddrValve(IP 过滤)/ErrorReportValve(错误页);配置 server.xml/context.xml;业务开发用 Filter(可移植)、Tomcat 层面通用处理用内置 Valve」。
七、常见误区与追问
- 误区:Valve 和 Filter 是一回事。 层次不同——Valve 是 Tomcat 私有的容器级拦截器(在 Tomcat 架构的 Pipeline 上,Servlet 规范之外,不可移植,换 Jetty/Undertow 就没有);Filter 是 Servlet 标准的应用级拦截器(任何 Servlet 容器都支持,可移植);执行顺序上 Valve 在 Filter 之前(更早、更底层)。
- 误区:Valve 可以在任何 Servlet 容器用。 不能——Valve 是 Tomcat 私有的,属于 Tomcat 自己的架构(Pipeline + Valve);换成 Jetty、Undertow 就没有 Valve 了(不可移植);要可移植的拦截处理用 Filter(Servlet 标准)。
- 误区:业务开发应该多用 Valve。 不——业务开发应该用 Filter(Servlet 标准、可移植、简单,认证、编码、CORS 等);Valve 是 Tomcat 私有的、容器级的,主要用于 Tomcat 运维/底层配置(访问日志、真实 IP、IP 过滤),且不可移植;除非确实需要「Tomcat 容器级、跨应用」的处理才用 Valve。
- 误区:Filter 在 Valve 之前执行。 反了——Valve 在 Filter 之前执行:请求进来先经过 Tomcat 的各级 Valve(Engine→Host→Context→Wrapper),然后才进入 Servlet 规范的 Filter 链,最后到 Servlet;因为 Valve 在 Tomcat 容器层(更外层、更早)、Filter 在应用层(进入应用后)。
- 追问:Tomcat 的 Valve 和 Servlet 的 Filter 有什么区别? 层次不同:Valve 是 Tomcat 私有的、容器级的拦截器——它在 Tomcat 的架构里(每个容器组件 Engine/Host/Context/Wrapper 都有一个 Pipeline,Pipeline 上串着 Valve),在 Servlet 规范之外、执行更早(请求到达 Servlet 应用、Filter 之前)、可以作用于多个应用(Engine/Host 级)、不可移植(换容器就没有);Filter 是 Servlet 标准规范的、应用级的拦截器——作用于单个 Web 应用内、在 Servlet 之前(但 Valve 之后)、任何 Servlet 容器都支持(可移植);请求顺序是 Valve → Filter → Servlet。
- 追问:什么时候该用 Valve,什么时候用 Filter? 用 Valve:需要 Tomcat 层面的、跨应用的通用处理(如整个 Tomcat 的访问日志 AccessLogValve、IP 白名单 RemoteAddrValve、代理后的真实 IP RemoteIpValve),主要在 Tomcat 运维/配置时用,且不介意不可移植;用 Filter:应用内的处理(认证、字符编码、CORS、业务相关),需要可移植(换容器不受影响),这是大多数业务开发的选择;简单说,Tomcat 层面通用配置用内置 Valve,应用内业务处理用 Filter。
- 追问:RemoteIpValve 有什么用? 处理代理/负载均衡后的真实客户端 IP——当应用部署在 Nginx、负载均衡器后面时,request.getRemoteAddr() 拿到的是代理/负载均衡器的 IP,不是真实客户端 IP;真实 IP 在 X-Forwarded-For 请求头里;RemoteIpValve 会从 X-Forwarded-For 头解析出真实的客户端 IP,替换 request 的 remoteAddr,这样应用里 request.getRemoteAddr() 就能拿到真实客户端 IP;这是部署在代理后面时常用的 Valve。
八、加强记忆
Valve(阀门)是 Tomcat 特有的「请求处理拦截机制」——像一道道阀门串在 Tomcat 请求处理管道(Pipeline)上,请求依次流过做处理(责任链模式);Tomcat 的每个容器组件(Engine/Host/Context/Wrapper)都有自己的 Pipeline 和 Valve。和 Filter 的核心区别是「层次不同」:Valve 是 Tomcat 容器级的——属于 Tomcat 自己的架构、在 Servlet 规范之外(Tomcat 私有)、执行更早(请求到 Servlet 应用、Filter 之前)、可跨应用(Engine/Host 级)、不可移植(换 Jetty/Undertow 就没有);Filter 是 Servlet 规范级的——属于标准、作用于 Web 应用内、任何 Servlet 容器支持(可移植)、在 Servlet 之前(但 Valve 之后)。请求顺序:连接器 → Tomcat 的 Valve(各级)→ Filter → Servlet(Valve 在 Filter 之前、更底层)。常用内置 Valve:AccessLogValve(访问日志)、RemoteIpValve(代理后解析真实 IP)、RemoteAddrValve(IP 过滤白名单)、ErrorReportValve(错误页);配置在 server.xml/context.xml。选择:业务开发用 Filter(可移植、标准)、Tomcat 层面的通用处理用内置 Valve(访问日志、真实 IP)。一句话「Valve=Tomcat 私有的容器级拦截器(Pipeline 上的阀门,每层容器有 Pipeline+Valve,责任链);vs Filter 层次不同:Valve(Tomcat 私有/容器级/执行更早/可跨应用/不可移植)、Filter(Servlet 标准/应用级/可移植);顺序连接器→Valve→Filter→Servlet(Valve 更早);内置 AccessLogValve/RemoteIpValve;业务用 Filter、Tomcat 通用处理用 Valve」。