本文纲要
Filter概述Filter快速入门
开发步骤
项目结构
核心代码Filter执行流程
流程图解
代码演示
数据处理时机Filter拦截路径配置
四种配置方式
示例演示- 过滤器链
概念与执行流程
优先级规则
示例代码与输出 - 案例:登录验证过滤器
需求分析
项目结构
登录验证流程
完整代码解析 Listener概述Listener基本使用
开发步骤
代码示例- 总结
Filter概述
在JavaWeb中,有三大核心组件:Servlet、Filter 和 Listener。
Filter(过滤器) 的作用是拦截对Web资源的请求,并在请求到达目标资源前或响应返回客户端前进行一些通用的、重复性的处理。
常见应用场景包括:
- 登录验证:统一判断用户是否已登录
- 权限控制:细粒度访问权限校验
- 统一编码处理:避免每个Servlet重复设置编码
- 敏感词过滤:对请求参数或响应内容进行过滤
过滤器的工作模型如下:
#mermaid-svg-qLNoMtmtn1PggUZr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qLNoMtmtn1PggUZr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qLNoMtmtn1PggUZr .error-icon{fill:#552222;}#mermaid-svg-qLNoMtmtn1PggUZr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qLNoMtmtn1PggUZr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qLNoMtmtn1PggUZr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qLNoMtmtn1PggUZr .marker.cross{stroke:#333333;}#mermaid-svg-qLNoMtmtn1PggUZr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qLNoMtmtn1PggUZr p{margin:0;}#mermaid-svg-qLNoMtmtn1PggUZr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qLNoMtmtn1PggUZr .cluster-label text{fill:#333;}#mermaid-svg-qLNoMtmtn1PggUZr .cluster-label span{color:#333;}#mermaid-svg-qLNoMtmtn1PggUZr .cluster-label span p{background-color:transparent;}#mermaid-svg-qLNoMtmtn1PggUZr .label text,#mermaid-svg-qLNoMtmtn1PggUZr span{fill:#333;color:#333;}#mermaid-svg-qLNoMtmtn1PggUZr .node rect,#mermaid-svg-qLNoMtmtn1PggUZr .node circle,#mermaid-svg-qLNoMtmtn1PggUZr .node ellipse,#mermaid-svg-qLNoMtmtn1PggUZr .node polygon,#mermaid-svg-qLNoMtmtn1PggUZr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qLNoMtmtn1PggUZr .rough-node .label text,#mermaid-svg-qLNoMtmtn1PggUZr .node .label text,#mermaid-svg-qLNoMtmtn1PggUZr .image-shape .label,#mermaid-svg-qLNoMtmtn1PggUZr .icon-shape .label{text-anchor:middle;}#mermaid-svg-qLNoMtmtn1PggUZr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qLNoMtmtn1PggUZr .rough-node .label,#mermaid-svg-qLNoMtmtn1PggUZr .node .label,#mermaid-svg-qLNoMtmtn1PggUZr .image-shape .label,#mermaid-svg-qLNoMtmtn1PggUZr .icon-shape .label{text-align:center;}#mermaid-svg-qLNoMtmtn1PggUZr .node.clickable{cursor:pointer;}#mermaid-svg-qLNoMtmtn1PggUZr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qLNoMtmtn1PggUZr .arrowheadPath{fill:#333333;}#mermaid-svg-qLNoMtmtn1PggUZr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qLNoMtmtn1PggUZr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qLNoMtmtn1PggUZr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qLNoMtmtn1PggUZr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qLNoMtmtn1PggUZr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qLNoMtmtn1PggUZr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qLNoMtmtn1PggUZr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qLNoMtmtn1PggUZr .cluster text{fill:#333;}#mermaid-svg-qLNoMtmtn1PggUZr .cluster span{color:#333;}#mermaid-svg-qLNoMtmtn1PggUZr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qLNoMtmtn1PggUZr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qLNoMtmtn1PggUZr rect.text{fill:none;stroke-width:0;}#mermaid-svg-qLNoMtmtn1PggUZr .icon-shape,#mermaid-svg-qLNoMtmtn1PggUZr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qLNoMtmtn1PggUZr .icon-shape p,#mermaid-svg-qLNoMtmtn1PggUZr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qLNoMtmtn1PggUZr .icon-shape .label rect,#mermaid-svg-qLNoMtmtn1PggUZr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qLNoMtmtn1PggUZr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qLNoMtmtn1PggUZr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qLNoMtmtn1PggUZr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 拦截
客户端请求
Filter
预处理
目标资源
后处理
客户端响应
凡是访问Web应用中的资源(Servlet、JSP、HTML等),都可以先经过过滤器,再决定是否放行。
Filter快速入门
1 ) 开发步骤
- 定义一个类,实现
javax.servlet.Filter接口 - 重写三个方法:
init()、doFilter()、destroy()init():初始化时调用doFilter():每次拦截请求时执行,是过滤器的核心destroy():销毁时调用
- 使用
@WebFilter注解配置拦截路径(注意:这里的路径是拦截资源的路径,不是过滤器的访问路径) - 在
doFilter()中编写逻辑,并通过chain.doFilter(request, response)放行
2 ) 项目结构
dir
filter-demo
├── pom.xml
├── src
│ └── main
│ ├── java/com/wb/web/filter
│ │ ├── FilterDemo.java
│ │ ├── FilterDemo2.java
│ │ └── AFilterDemo2.java
│ └── webapp
│ ├── hello.jsp
│ ├── index.jsp
│ └── WEB-INF/web.xml
3 ) 核心代码
3.1 定义一个简单的过滤器
java
package com.wb.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 1. 放行前,对 request 数据进行处理
System.out.println("1.FilterDemo...");
// 放行
chain.doFilter(request, response);
// 2. 放行后,对 Response 数据进行处理
System.out.println("5.FilterDemo...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}
注意:@WebFilter("/*") 表示拦截所有资源。如果拦截后不调用 chain.doFilter(),请求将不会到达目标资源。
3.2 目标 JSP(hello.jsp)
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title></head>
<body>
<h1>hello JSP~</h1>
<%
System.out.println("3.hello jsp");
%>
</body>
</html>
访问 hello.jsp 时,控制台输出顺序为:1.FilterDemo... → 3.hello jsp → 5.FilterDemo...,证明过滤器先执行放行前逻辑,再访问资源,最后执行放行后逻辑。
Filter执行流程
过滤器的执行流程可以概括为:放行前逻辑 → 放行 → 访问资源 → 放行后逻辑。
目标资源 过滤器 客户端 目标资源 过滤器 客户端 #mermaid-svg-zK2SnYWWxzUPKTAN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zK2SnYWWxzUPKTAN .error-icon{fill:#552222;}#mermaid-svg-zK2SnYWWxzUPKTAN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zK2SnYWWxzUPKTAN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zK2SnYWWxzUPKTAN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zK2SnYWWxzUPKTAN .marker.cross{stroke:#333333;}#mermaid-svg-zK2SnYWWxzUPKTAN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zK2SnYWWxzUPKTAN p{margin:0;}#mermaid-svg-zK2SnYWWxzUPKTAN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zK2SnYWWxzUPKTAN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-zK2SnYWWxzUPKTAN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-zK2SnYWWxzUPKTAN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-zK2SnYWWxzUPKTAN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-zK2SnYWWxzUPKTAN .sequenceNumber{fill:white;}#mermaid-svg-zK2SnYWWxzUPKTAN #sequencenumber{fill:#333;}#mermaid-svg-zK2SnYWWxzUPKTAN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-zK2SnYWWxzUPKTAN .messageText{fill:#333;stroke:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zK2SnYWWxzUPKTAN .labelText,#mermaid-svg-zK2SnYWWxzUPKTAN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .loopText,#mermaid-svg-zK2SnYWWxzUPKTAN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-zK2SnYWWxzUPKTAN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-zK2SnYWWxzUPKTAN .noteText,#mermaid-svg-zK2SnYWWxzUPKTAN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-zK2SnYWWxzUPKTAN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zK2SnYWWxzUPKTAN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zK2SnYWWxzUPKTAN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-zK2SnYWWxzUPKTAN .actorPopupMenu{position:absolute;}#mermaid-svg-zK2SnYWWxzUPKTAN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-zK2SnYWWxzUPKTAN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-zK2SnYWWxzUPKTAN .actor-man circle,#mermaid-svg-zK2SnYWWxzUPKTAN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-zK2SnYWWxzUPKTAN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 放行前逻辑 执行业务逻辑 放行后逻辑 请求chain.doFilter() 放行返回响应
代码演示
在过滤器 FilterDemo 和目标 JSP 中分别打印标记:
java
// FilterDemo.java
System.out.println("1.FilterDemo..."); // 放行前
chain.doFilter(request, response);
System.out.println("5.FilterDemo..."); // 放行后
jsp
<%-- hello.jsp --%>
<%
System.out.println("3.hello jsp");
%>
输出结果:1.FilterDemo... → 3.hello jsp → 5.FilterDemo...,验证了上述执行顺序。
数据处理时机
- 放行前:一般对
request数据进行处理(如编码设置、参数过滤)。因为此时response还没有业务数据。 - 放行后:一般对
response数据进行处理(如压缩、敏感词替换)。因为资源执行完毕后response中才填充了真正的响应内容。
Filter拦截路径配置
使用 @WebFilter 注解的 value 属性(或 urlPatterns)配置拦截路径,支持四种方式:
| 配置方式 | 示例 | 说明 |
|---|---|---|
| 具体资源路径拦截 | "/login.jsp" |
只有访问 login.jsp 时过滤器才生效 |
| 目录拦截 | "/user/*" |
访问 /user/ 下所有资源时触发 |
| 后缀名拦截 | "*.jsp" |
访问所有 .jsp 后缀资源时触发 |
| 拦截所有 | "/*" |
任何资源请求都会被过滤(最常用) |
示例演示
1 ) 拦截特定资源
java
@WebFilter("/hello.jsp")
public class FilterDemo2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("FilterDemo2 executed...");
chain.doFilter(request, response);
}
//...
}
此时只有访问 hello.jsp 时控制台才输出信息,访问 index.jsp 则不触发过滤器。
2 ) 拦截所有资源
java
@WebFilter("/*")
public class FilterDemo implements Filter { ... }
这种方式在统一登录验证、编码处理等场景下最常用。
过滤器链
1 ) 概念与执行流程
一个Web应用可以配置多个过滤器 ,它们按照一定顺序形成一个过滤器链 。请求会依次经过每个过滤器,直到最后一个过滤器放行后到达目标资源,返回时再按相反顺序执行放行后的逻辑。
#mermaid-svg-CU7q0TjSKFTdvFCm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CU7q0TjSKFTdvFCm .error-icon{fill:#552222;}#mermaid-svg-CU7q0TjSKFTdvFCm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CU7q0TjSKFTdvFCm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CU7q0TjSKFTdvFCm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CU7q0TjSKFTdvFCm .marker.cross{stroke:#333333;}#mermaid-svg-CU7q0TjSKFTdvFCm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CU7q0TjSKFTdvFCm p{margin:0;}#mermaid-svg-CU7q0TjSKFTdvFCm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster-label text{fill:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster-label span{color:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster-label span p{background-color:transparent;}#mermaid-svg-CU7q0TjSKFTdvFCm .label text,#mermaid-svg-CU7q0TjSKFTdvFCm span{fill:#333;color:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm .node rect,#mermaid-svg-CU7q0TjSKFTdvFCm .node circle,#mermaid-svg-CU7q0TjSKFTdvFCm .node ellipse,#mermaid-svg-CU7q0TjSKFTdvFCm .node polygon,#mermaid-svg-CU7q0TjSKFTdvFCm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CU7q0TjSKFTdvFCm .rough-node .label text,#mermaid-svg-CU7q0TjSKFTdvFCm .node .label text,#mermaid-svg-CU7q0TjSKFTdvFCm .image-shape .label,#mermaid-svg-CU7q0TjSKFTdvFCm .icon-shape .label{text-anchor:middle;}#mermaid-svg-CU7q0TjSKFTdvFCm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CU7q0TjSKFTdvFCm .rough-node .label,#mermaid-svg-CU7q0TjSKFTdvFCm .node .label,#mermaid-svg-CU7q0TjSKFTdvFCm .image-shape .label,#mermaid-svg-CU7q0TjSKFTdvFCm .icon-shape .label{text-align:center;}#mermaid-svg-CU7q0TjSKFTdvFCm .node.clickable{cursor:pointer;}#mermaid-svg-CU7q0TjSKFTdvFCm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CU7q0TjSKFTdvFCm .arrowheadPath{fill:#333333;}#mermaid-svg-CU7q0TjSKFTdvFCm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CU7q0TjSKFTdvFCm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CU7q0TjSKFTdvFCm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CU7q0TjSKFTdvFCm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CU7q0TjSKFTdvFCm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CU7q0TjSKFTdvFCm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster text{fill:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm .cluster span{color:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CU7q0TjSKFTdvFCm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CU7q0TjSKFTdvFCm rect.text{fill:none;stroke-width:0;}#mermaid-svg-CU7q0TjSKFTdvFCm .icon-shape,#mermaid-svg-CU7q0TjSKFTdvFCm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CU7q0TjSKFTdvFCm .icon-shape p,#mermaid-svg-CU7q0TjSKFTdvFCm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CU7q0TjSKFTdvFCm .icon-shape .label rect,#mermaid-svg-CU7q0TjSKFTdvFCm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CU7q0TjSKFTdvFCm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CU7q0TjSKFTdvFCm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CU7q0TjSKFTdvFCm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端请求
Filter1 放行前
Filter2 放行前
目标资源
Filter2 放行后
Filter1 放行后
客户端响应
2 ) 优先级规则
当使用 @WebFilter 注解配置时,过滤器的执行顺序由类名字符串的自然排序决定。例如:
AFilterDemo2会优于FilterDemo执行FilterDemo会优于FilterDemo2执行(因为Demo<Demo2)
3 ) 示例代码与输出
过滤器1:FilterDemo
java
@WebFilter("/*")
public class FilterDemo implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("1.FilterDemo...");
chain.doFilter(request, response);
System.out.println("5.FilterDemo...");
}
//...
}
过滤器2:FilterDemo2
java
@WebFilter("/*")
public class FilterDemo2 implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("2.FilterDemo...");
chain.doFilter(request, response);
System.out.println("4.FilterDemo...");
}
//...
}
访问任意资源时,控制台输出:
log
1.FilterDemo...
2.FilterDemo...
3.hello jsp
4.FilterDemo...
5.FilterDemo...
体现了 FilterDemo(类名字典序更小)先执行,且放行后按反向顺序执行。
案例:登录验证过滤器
1 ) 需求分析
在没有登录验证时,用户可以不登录直接访问品牌列表等受保护资源,登录功能形同虚设。
我们需要编写一个全局过滤器,对所有资源进行登录校验:
- 已登录:放行
- 未登录:跳转到登录页面,并提示"您尚未登录"
同时,必须将登录/注册相关的资源直接放行,否则这些页面的CSS、图片等也会被拦截,导致无法登录。
2 ) 项目结构
dir
brand-demo
├── pom.xml
├── src/main/java/com/wb
│ ├── web
│ │ ├── filter
│ │ │ └── LoginFilter.java # 登录验证过滤器
│ │ ├── listener
│ │ │ └── ContextLoaderListener.java # 监听器示例
│ │ ├── LoginServlet.java
│ │ ├── RegisterServlet.java
│ │ ├── SelectAllServlet.java
│ │ └── ... (其他Servlet)
│ └── ... (service, mapper, pojo, util)
└── src/main/webapp
├── login.jsp
├── register.jsp
├── brand.jsp
└── ...
3 ) 登录验证流程
#mermaid-svg-x0xnHjv5ynDbDzf5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x0xnHjv5ynDbDzf5 .error-icon{fill:#552222;}#mermaid-svg-x0xnHjv5ynDbDzf5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x0xnHjv5ynDbDzf5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .marker.cross{stroke:#333333;}#mermaid-svg-x0xnHjv5ynDbDzf5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x0xnHjv5ynDbDzf5 p{margin:0;}#mermaid-svg-x0xnHjv5ynDbDzf5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster-label text{fill:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster-label span{color:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster-label span p{background-color:transparent;}#mermaid-svg-x0xnHjv5ynDbDzf5 .label text,#mermaid-svg-x0xnHjv5ynDbDzf5 span{fill:#333;color:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .node rect,#mermaid-svg-x0xnHjv5ynDbDzf5 .node circle,#mermaid-svg-x0xnHjv5ynDbDzf5 .node ellipse,#mermaid-svg-x0xnHjv5ynDbDzf5 .node polygon,#mermaid-svg-x0xnHjv5ynDbDzf5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .rough-node .label text,#mermaid-svg-x0xnHjv5ynDbDzf5 .node .label text,#mermaid-svg-x0xnHjv5ynDbDzf5 .image-shape .label,#mermaid-svg-x0xnHjv5ynDbDzf5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-x0xnHjv5ynDbDzf5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .rough-node .label,#mermaid-svg-x0xnHjv5ynDbDzf5 .node .label,#mermaid-svg-x0xnHjv5ynDbDzf5 .image-shape .label,#mermaid-svg-x0xnHjv5ynDbDzf5 .icon-shape .label{text-align:center;}#mermaid-svg-x0xnHjv5ynDbDzf5 .node.clickable{cursor:pointer;}#mermaid-svg-x0xnHjv5ynDbDzf5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .arrowheadPath{fill:#333333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0xnHjv5ynDbDzf5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x0xnHjv5ynDbDzf5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0xnHjv5ynDbDzf5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster text{fill:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 .cluster span{color:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-x0xnHjv5ynDbDzf5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x0xnHjv5ynDbDzf5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-x0xnHjv5ynDbDzf5 .icon-shape,#mermaid-svg-x0xnHjv5ynDbDzf5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0xnHjv5ynDbDzf5 .icon-shape p,#mermaid-svg-x0xnHjv5ynDbDzf5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x0xnHjv5ynDbDzf5 .icon-shape .label rect,#mermaid-svg-x0xnHjv5ynDbDzf5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0xnHjv5ynDbDzf5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x0xnHjv5ynDbDzf5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x0xnHjv5ynDbDzf5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
有
无
收到请求
请求路径是否
与登录/注册相关?
直接放行
Session中
是否有user对象?
放行
存储提示信息
转发到login.jsp
4 ) 完整代码解析
LoginFilter.java
java
package com.wb.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
// 1. 判断访问路径是否与登录/注册相关(应该直接放行)
String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet",
"/register.jsp","/registerServlet","/checkCodeServlet"};
String url = req.getRequestURL().toString();
for (String u : urls) {
if (url.contains(u)) {
// 直接放行,不再执行后续判断
chain.doFilter(request, response);
return;
}
}
// 2. 判断Session中是否存在user对象
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
if (user != null) {
// 已登录,放行
chain.doFilter(request, response);
} else {
// 未登录,存储提示信息并跳转到登录页
req.setAttribute("login_msg", "您尚未登录!");
req.getRequestDispatcher("/login.jsp").forward(req, response);
}
}
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
}
要点分析:
- 先将
ServletRequest强转为HttpServletRequest,以便获取 Session 和请求路径。 - 白名单路径使用
return直接退出方法,避免代码继续向下执行。 - 如果用户未登录,设置
login_msg属性后转发到login.jsp,前端通过${loginmsg}显示提示。 - 登录成功后,
LoginServlet会将user对象存入 Session,后续请求就能通过过滤器。
5 ) 登录成功后设置 Session:
java
// LoginServlet 片段
HttpSession session = request.getSession();
session.setAttribute("user", user);
这样,过滤器就可以通过 session.getAttribute("user") 判断登录状态。
Listener概述
Listener(监听器) 是 JavaWeb 三大组件之一,用于监听 ServletContext、HttpSession、ServletRequest 等域对象的创建 、销毁 以及属性变化。当监听到指定事件时会自动执行相应方法。
虽然现在监听器使用频率不如 Filter 和 Servlet 高,但在框架初始化等场景下仍非常重要(例如 Spring 利用监听器加载上下文)。
JavaWeb 提供了 8 个监听器接口:
| 监听对象 | 监听器接口(创建/销毁) | 监听器接口(属性变化) |
|---|---|---|
| ServletContext | ServletContextListener | ServletContextAttributeListener |
| HttpSession | HttpSessionListener | HttpSessionAttributeListener |
| HttpSession | HttpSessionActivationListener(钝化/活化) | HttpSessionBindingListener(绑定) |
| ServletRequest | ServletRequestListener | ServletRequestAttributeListener |
本文重点介绍最常用的 ServletContextListener
Listener基本使用
1 ) 开发步骤
- 定义一个类,实现对应的监听器接口(如
ServletContextListener) - 重写响应方法(如
contextInitialized()、contextDestroyed()) - 添加
@WebListener注解(无需配置路径,自动生效)
2 ) 代码示例
ContextLoaderListener.java
java
package com.wb.web.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Web应用启动时加载资源
System.out.println("ContextLoaderListener...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Web应用关闭时释放资源
}
}
当应用启动时,控制台会输出 ContextLoaderListener...,表明监听器已自动执行。
ServletContext 对象代表整个Web应用,它的初始化标志着应用加载成功,因此可以在 contextInitialized() 中执行一些全局初始化操作(如加载配置文件、建立数据库连接池等)。
总结
- Filter(过滤器) :拦截请求,实现通用预处理和后处理,通过
@WebFilter配置拦截路径,通过chain.doFilter()放行,多个过滤器形成链式调用,执行顺序由类名的自然排序决定。 - Listener(监听器) :监听域对象的生命周期和属性变化,通过
@WebListener自动注册,常用于应用启动时的资源加载。 - 两者配合 Servlet 构成了 JavaWeb 开发的三大基石,理解它们能够帮助我们编写更加优雅、可维护的 Web 应用。
以上便是 JavaWeb 中 Filter 与 Listener 的核心知识,通过本文的学习,你已经掌握了过滤器的开发、执行流程、路径配置、过滤器链以及实战登录验证案例,同时也了解了监听器的基本用法。
在实际项目中,这些组件将极大程度地简化重复代码,提升开发效率。