
🌈 个人主页: Hygge_Code
🔥 热门专栏:从0开始学习Java | Linux学习| 计算机网络
💫 个人格言: "既然选择了远方,便不顾风雨兼程"

文章目录
- Filter------Web请求的过滤与拦截
-
- 一、Filter的核心概念
- 二、Filter的体系结构🤔
- [三、重要接口 📖](#三、重要接口 📖)
-
- [1. Filter接口的核心方法](#1. Filter接口的核心方法)
- [2. FilterConfig接口的核心方法](#2. FilterConfig接口的核心方法)
- [3. FilterChain接口的作用🍂](#3. FilterChain接口的作用🍂)
- [4. HttpFilter抽象类的核心方法](#4. HttpFilter抽象类的核心方法)
- [四、Filter 三种配置方式🧾🐦🔥🧾](#四、Filter 三种配置方式🧾🐦🔥🧾)
- [1. 注解无参数配置](#1. 注解无参数配置)
- [2. 注解带参数配置](#2. 注解带参数配置)
- [3. web.xml 配置(兼容所有版本)](#3. web.xml 配置(兼容所有版本))
- 五、Filter的应用
- 六、FilterChain过滤器执行细节⚠️🧐⚠️
Filter------Web请求的过滤与拦截
一、Filter的核心概念
Filter(过滤器)是运行在服务器端的组件,用于拦截客户端的请求和响应,对请求和响应信息进行预处理或后处理。Filter不直接处理请求,而是对请求进行过滤(如编码转换、登录验证、权限控制)后,将请求传递给后续的Servlet或JSP。
二、Filter的体系结构🤔

三、重要接口 📖
1. Filter接口的核心方法
Filter接口定义了Filter的生命周期方法,由Servlet容器负责调用:
- 初始化方法:
default void init(FilterConfig filterConfig) throws ServletException;------Filter实例创建后调用,用于读取配置参数(默认实现为空,可重写) - 过滤方法:
void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException;------核心方法,用于拦截和处理请求/响应 - 销毁方法:
default void destroy();------Filter容器关闭时调用,用于释放资源(默认实现为空,可重写)
2. FilterConfig接口的核心方法
FilterConfig接口用于获取Filter的初始化配置信息,核心方法如下:
- 获取Filter名称:
String getFilterName();------返回web.xml中配置的<filter-name>值 - 获取Servlet上下文:
ServletContext getServletContext();------返回当前Web应用的ServletContext对象 - 获取指定初始化参数:
String getInitParameter(String parameterName);------返回web.xml中配置的指定名称的初始化参数值 - 获取所有初始化参数名称:
Enumeration<String> getInitParameterNames();------返回所有初始化参数名称的枚举集合
3. FilterChain接口的作用🍂
FilterChain(过滤器链)用于维护多个Filter的执行顺序,核心方法为:void doFilter(ServletRequest req, ServletResponse resp);
- 当一个Filter处理完请求后,调用
chain.doFilter(req, resp)可将请求传递给过滤器链中的下一个Filter - 若当前Filter是过滤器链中的最后一个,则将请求传递给目标Servlet或JSP
- 若未调用
chain.doFilter(req, resp),请求将被拦截,不会传递给后续组件
4. HttpFilter抽象类的核心方法
HttpFilter是HTTP协议专用的Filter抽象类,核心方法如下:
- 重写
doFilter(ServletRequest req, ServletResponse resp, FilterChain chain):将请求和响应对象强转为HttpServletRequest和HttpServletResponse,调用重载的doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)方法 - 重载
doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain):HTTP协议专用的过滤方法,子类可重写该方法处理HTTP请求
四、Filter 三种配置方式🧾🐦🔥🧾
Filter 配置的核心目的就两个:告诉服务器"哪个Filter类要生效" + 指定"拦截哪些请求",三种配置方式只是实现这两个目的的不同途径
不管哪种配置,都要先写一个 Filter 类(实现 Filter 接口,重写 doFilter 核心方法),配置只是"激活"这个类的手段。
1. 注解无参数配置
适用场景
逻辑简单、不需要自定义参数(比如单纯记录日志、固定拦截某类请求)。
写法特点
只需要在 Filter 类上贴一个注解,不用写任何 XML,相当于"给类贴个标签,告诉服务器:你要生效,且拦截这些请求"。
步骤
- 写 Filter 类,实现
Filter接口; - 在类上添加
@WebFilter(urlPatterns = "拦截路径"); - 在
doFilter里写核心逻辑,最后必须调用chain.doFilter(...)放行。
例子(记录所有请求日志)
java
// 注解直接指定"拦截所有请求(/*)",无其他配置
@WebFilter(urlPatterns = "/*")
public class LogFilter implements Filter {
// 核心拦截方法:每次请求都会执行
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("有人访问网站啦!"); // 简单日志逻辑
chain.doFilter(request, response); // 放行:让请求继续到目标资源(比如Servlet、JSP)
}
// 初始化和销毁方法:不用写逻辑,空实现即可
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
关键
- 注解里只需要写
urlPatterns(拦截路径),其他都能省; - 服务器启动时会自动扫描这个注解,自动激活 Filter,不用手动配置。
2. 注解带参数配置
适用场景
需要动态配置参数(比如编码格式、登录页路径,不想写死在代码里)。
写法特点
还是用 @WebFilter 注解,但多了 initParams 属性,相当于"给类贴标签时,顺便传点参数,类里能直接用"。
步骤
- 写 Filter 类,实现
Filter接口; - 注解里加
initParams = @WebInitParam(name="参数名", value="参数值")(多个参数用逗号分隔); - 在
init方法里,通过FilterConfig获取注解里的参数; - 在
doFilter里使用参数,最后放行。
例子(动态配置编码格式)
java
// 注解指定"拦截所有请求",同时传参数(encoding=UTF-8)
@WebFilter(
urlPatterns = "/*",
initParams = @WebInitParam(name = "encoding", value = "UTF-8") // 自定义参数:编码格式
)
public class EncodingFilter implements Filter {
private String encoding; // 存储注解里的参数
// 初始化方法:服务器启动时执行1次,用于获取参数
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从注解的参数中获取"encoding"的值(这里是UTF-8)
encoding = filterConfig.getInitParameter("encoding");
}
// 核心拦截方法:使用参数设置编码
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding(encoding); // 用配置的参数设置请求编码
chain.doFilter(request, response); // 放行
}
@Override
public void destroy() {}
}
关键
- 参数存在注解里,修改时不用改核心逻辑,只改注解的
value即可; - 必须通过
FilterConfig.getInitParameter("参数名")获取参数。
3. web.xml 配置(兼容所有版本)
适用场景
项目用的是低版本 Servlet(比如 Servlet 2.5 及以下),或者想把所有配置集中在 web.xml 里统一管理。
写法特点
Filter 类上不贴任何注解,所有配置都写在 web.xml 文件里,相当于"在配置文件里告诉服务器:要激活哪个Filter类、给它传什么参数、拦截哪些请求"。
步骤
- 写纯 Filter 类(无任何注解);
- 在
web.xml里分两步配置:- 第一步:用
<filter>标签注册 Filter 类,指定类的全路径,可选传参数; - 第二步:用
<filter-mapping>标签绑定拦截路径(和上面注册的类同名关联);
- 第一步:用
- 类里通过
FilterConfig获取web.xml里的参数,doFilter里写逻辑并放行。
例子(和上面注解带参数功能一样,编码配置)
- 纯 Filter 类(无注解):
java
public class EncodingFilter implements Filter {
private String encoding;
// 从web.xml里获取参数
@Override
public void init(FilterConfig filterConfig) throws ServletException {
encoding = filterConfig.getInitParameter("encoding");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
- web.xml 配置(核心):
xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="4.0">
<!-- 第一步:注册Filter类 + 配置参数 -->
<filter>
<filter-name>EncodingFilter</filter-name> <!-- 给Filter起个名字(随便起,但要和下面一致) -->
<filter-class>com.example.EncodingFilter</filter-class> <!-- Filter的全类名(包名+类名) -->
<!-- 可选:配置参数(和注解的initParams一样) -->
<init-param>
<param-name>encoding</param-name> <!-- 参数名 -->
<param-value>UTF-8</param-value> <!-- 参数值 -->
</init-param>
</filter>
<!-- 第二步:配置拦截路径(和上面的Filter名字绑定) -->
<filter-mapping>
<filter-name>EncodingFilter</filter-name> <!-- 必须和上面的名字一致,否则找不到类 -->
<url-pattern>/*</url-pattern> <!-- 拦截所有请求 -->
</filter-mapping>
</web-app>
总结
| 配置方式 | 核心操作 | 优点 | 适用场景 |
|---|---|---|---|
| 注解无参数 | 类上贴 @WebFilter(urlPatterns="路径") |
最简洁,不用写XML | 简单逻辑(日志、固定拦截) |
| 注解带参数 | 注解里加 initParams 传参数,类内获取 |
简洁又灵活,参数可配 | 需动态参数(编码、路径) |
| web.xml配置 | 配置文件分 <filter> 和 <filter-mapping> |
兼容所有版本,统一管理 | 低版本Servlet、集中配置 |
五、Filter的应用
场景1:中文乱码处理(CharacterEncodingFilter)
中文乱码的核心原因是请求/响应的字符编码不一致,通过Filter可统一设置编码:
java
package com.cyx.jsp.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
private String characterEncoding; // 存储编码格式
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("过滤器初始化");
// 读取web.xml中配置的初始化参数
this.characterEncoding = config.getInitParameter("characterEncoding");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器过滤操作");
// 设置请求字符编码
request.setCharacterEncoding(characterEncoding);
// 设置响应字符编码
response.setCharacterEncoding(characterEncoding);
// 将请求传递给下一个Filter或Servlet(必须调用,否则请求被拦截)
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}
- 这是过滤器的核心方法,每次客户端请求匹配的资源时都会执行
- **核心功能:**在请求到达 Servlet 之前,统一设置请求(request)和响应(response)的字符编码(如 UTF-8),确保请求参数的解析和响应内容的输出都使用正确的编码,避免中文乱码
chain.doFilter(request, response):如果不调用这行代码,请求会被当前过滤器拦截,无法传递到后续的过滤器或 Servlet,导致请求处理中断
运行结果图示:
说明:UserInfoServlet类中并没有设置UTF-8编码,而在显示时却并没有乱码,说明编码设置在Filter过滤器中统一设置了
说明:过滤器仅初始化一次,并且在Servlet初始化前完成;过滤器操作了四次,对应了user中的四个属性;过滤器在Servlet销毁后也销毁
配置web.xml(注册字符编码过滤器)
xml
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.cyx.jsp.filter.CharacterEncodingFilter</filter-class>
<!-- 初始化参数:设置编码格式为UTF-8 -->
<init-param>
<param-name>characterEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 通配符/*:拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
场景2:登录超时处理(TimeoutFilter)
登录超时指用户登录后长时间未操作,session过期,此时需拦截请求并跳转到登录页:
java
package com.cyx.jsp.filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class TimeoutFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取当前会话(不创建新会话)
HttpSession session = request.getSession(false);
// 检查session是否存在且是否存储了用户名(登录标识)
if (session == null || session.getAttribute("username") == null) {
// 未登录或session过期,重定向到首页(登录页)
String homePageUrl = request.getContextPath();
if ("".equalsIgnoreCase(homePageUrl)) {
homePageUrl = "/"; // 上下文路径为空时,设置为根路径
}
response.sendRedirect(homePageUrl);
} else {
// 已登录且session有效,传递请求到下一个组件
chain.doFilter(request, response);
}
}
}
配置web.xml(注册登录超时过滤器)
xml
<filter>
<filter-name>timeoutFilter</filter-name>
<filter-class>com.cyx.jsp.filter.TimeoutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>timeoutFilter</filter-name>
<!-- 拦截所有请求(可根据需求调整,如仅拦截需要登录的路径) -->
<url-pattern>/*</url-pattern>
</filter-mapping>
六、FilterChain过滤器执行细节⚠️🧐⚠️

对应的代码示例🌰
步骤1:编写2个过滤器(Filter1、Filter2)
模拟图中的"前置代码、chain.doFilter、后置代码"逻辑:
Filter1
java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 拦截所有请求(/*),参与过滤器链
@WebFilter(urlPatterns = "/*")
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 前置代码(请求到达时执行)
System.out.println("Filter1:前置代码执行(请求阶段)");
// 2. 放行请求:执行下一个Filter/目标资源
chain.doFilter(request, response);
// 3. 后置代码(响应返回时执行)
System.out.println("Filter1:后置代码执行(响应阶段)");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
Filter2
java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 同样拦截所有请求,参与过滤器链
@WebFilter(urlPatterns = "/*")
public class Filter2 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 前置代码(请求到达时执行)
System.out.println("Filter2:前置代码执行(请求阶段)");
// 2. 放行请求:执行目标资源
chain.doFilter(request, response);
// 3. 后置代码(响应返回时执行)
System.out.println("Filter2:后置代码执行(响应阶段)");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
步骤2:编写Servlet
模拟一个简单的Servlet,作为请求的最终目标:
java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
// 目标资源:处理 /target 请求
@WebServlet(urlPatterns = "/target")
public class TargetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("<h1>目标资源(Servlet)已执行</h1>");
// 控制台打印,方便观察执行顺序
System.out.println("目标资源(TargetServlet):doGet方法执行");
}
}
运行结果
启动项目后,访问对应网址后,控制台运行结果如下:
Filter1:前置代码执行(请求阶段)
Filter2:前置代码执行(请求阶段)
目标资源(TargetServlet):doGet方法执行
Filter2:后置代码执行(响应阶段)
Filter1:后置代码执行(响应阶段)
代码对应图的核心说明
-
过滤器链顺序:
- 注解配置下,Filter按类名字母顺序 执行(
Filter1先于Filter2); - 若用
web.xml配置,顺序由<filter-mapping>的上下顺序决定。
- 注解配置下,Filter按类名字母顺序 执行(
-
chain.doFilter的作用:
- 执行
chain.doFilter(...)时,请求会跳转到下一个Filter (有Filter时),或跳转到目标资源(无Filter时); - 目标资源执行完后,会回到当前Filter的chain.doFilter之后的代码(即"后置代码")。
- 执行
-
图中的"前置/后置代码":
chain.doFilter之前的代码:请求到达时执行chain.doFilter之后的代码:响应返回时执行
如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!


