javax.servlet.Filter 介绍-笔记
1.javax.servlet.Filter
简介
javax.servlet.Filter
是 Java Servlet API 中的一个核心接口,用于在请求到达目标资源(如 Servlet 或 JSP)之前或响应返回给客户端之前执行预处理或后处理操作。它常用于实现与业务逻辑无关的通用功能,例如:
- 日志记录:记录请求和响应的详细信息。
- 身份验证和授权:检查用户权限,拦截未授权的访问。
- 字符编码设置:统一处理请求和响应的字符编码。
- 性能监控:统计请求的处理时间。
- 请求/响应内容修改:例如压缩响应内容或修改请求头。
Filter
接口定义如下:
package javax.servlet;import java.io.IOException;public interface Filter {void init(FilterConfig var1) throws ServletException;void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;void destroy();
}
Filter
接口包含以下三个方法:
-
init(FilterConfig config):
在 Filter 初始化时调用,用于读取 Filter 的初始化参数或执行其他初始化操作。 -
doFilter(ServletRequest request, ServletResponse response, FilterChain chain):
执行过滤逻辑的核心方法。开发者可以在此方法中:- 在调用
chain.doFilter()
之前执行前置处理(如权限检查)。 - 调用
chain.doFilter()
将请求传递给下一个 Filter 或目标资源。 - 在调用
chain.doFilter()
之后执行后置处理(如日志记录)。
- 在调用
-
destroy():
在 Filter 销毁时调用,用于释放资源或执行清理操作。
2. javax.servlet.Filter使用示例
2.1 代码示例
step1.定义Filter
定义一个用于
记录请求信息的Filter:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;public class LoggingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化操作(可选)System.out.println("LoggingFilter initialized.");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;// 前置处理:记录请求开始时间long startTime = System.currentTimeMillis();System.out.println("Request received: " + httpRequest.getRequestURI());// 继续处理链(调用下一个 Filter 或目标资源)chain.doFilter(request, response);// 后置处理:记录请求耗时long endTime = System.currentTimeMillis();System.out.println("Request completed in " + (endTime - startTime) + " ms");}@Overridepublic void destroy() {// 清理操作(可选)System.out.println("LoggingFilter destroyed.");}
}
step2. 配置Filter
LoggingFilter必须 显式注册到 Web 应用中 才能生效, Spring Boot中注册Filter有两种方式:
方式1:使用 @Component
(自动注册)
- Spring Boot 会自动识别实现了
Filter
接口的 Bean,并将其注册为过滤器。
import org.springframework.stereotype.Component;
import javax.servlet.Filter;@Component
public class LoggingFilter implements Filter {// 上述 LoggingFilter 实现的 doFilter 等方法...
}
方式2: 手动注册为 FilterRegistrationBean
- 对于有多个Filter的场景,该方式可以通过
FilterRegistrationBean.setOrder(int)
设置优先级(值越小越靠前)。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<TraceFilter> traceFilterRegistration() {FilterRegistrationBean<LoggingFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new LoggingFilter());registration.addUrlPatterns("/*"); // 拦截所有请求registration.setOrder(1); // 设置过滤器的顺序(可选)return registration;}
}
step3.测试 Filter
- 部署一个简单的 Servlet,并访问其 URL。
- 查看控制台输出,应能看到类似以下日志:
LoggingFilter initialized.
Request received: /example/hello
Request completed in 5 ms
2.2 注意事项
- Filter 顺序:多个 Filter 的执行顺序可由
FilterRegistrationBean.setOrder(int)
设置值决定(值越小越靠前);响应时顺序相反。 - 类型转换:
ServletRequest
和ServletResponse
需要转换为HttpServletRequest
和HttpServletResponse
以访问 HTTP 特定功能。 - 异常处理:在
doFilter
中抛出的异常会被 Servlet 容器捕获,需确保合理处理异常。
3.一个完整demo
在实际开发中,多个 Filter 的组合使用是常见的需求,在 Spring Boot 中,通过 Filter 可以优雅地实现多个通用功能的串联处理。下面我们将通过一个 “身份认证+ trace追踪 + 请求日志记录” 的典型业务场景,展示如何在 Spring Boot 中配置并使用多个 Filter,并说明它们的执行顺序。
3.1 业务场景说明
我们构建一个 Web 应用,要求如下:
- 身份认证(AuthFilter):检查请求头中的
Authorization
字段,若缺失或无效,返回 401。 - trace追踪(TraceFilter):在请求中添加traceId,便于后续追踪该请求
- 请求日志记录(LoggingFilter):记录请求处理耗时、请求路径等信息。
Filter 执行顺序应为:
AuthFilter
→ TraceFilter →LoggingFilter
即:先进行身份验证,验证通过后,再添加traceId,最后进行日志记录。
3.2 完整代码
step1. 定义Filter
身份认证 Filter:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class AuthFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑(可选)}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest)request;HttpServletResponse httpResponse = (HttpServletResponse)response;String authHeader = httpRequest.getHeader("Authorization");if (authHeader == null || !authHeader.equals("valid-token")) {// 鉴权失败,返回 401 Unauthorized; 这里演示方便直接打印一条日志//httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");//return;System.out.println("[AuthFilter] check authority failed");}// 继续处理请求chain.doFilter(request, response);}@Overridepublic void destroy() {// 销毁逻辑(可选)}
}
trace追踪 Filter
import java.io.IOException;
import java.util.UUID;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;public class TraceFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑(可选)}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest)request;// 生成唯一 traceIdString traceId = UUID.randomUUID().toString();// 将 traceId 放入httpRequesthttpRequest.setAttribute("traceId", traceId);// 继续处理请求chain.doFilter(request, response);}@Overridepublic void destroy() {// 销毁逻辑(可选)}
}
请求日志记录 Filter
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;public class LoggingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑(可选)}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;long startTime = System.currentTimeMillis();System.out.println("[LoggingFilter] Request started: " + httpRequest.getRequestURI());System.out.println("[LoggingFilter] Request traceId: " + httpRequest.getAttribute("traceId"));// 继续处理请求chain.doFilter(request, response);long endTime = System.currentTimeMillis();System.out.println("[LoggingFilter] Request completed in " + (endTime - startTime) + " ms");}@Overridepublic void destroy() {// 销毁逻辑(可选)}
}
step2. 配置Filter
使用Spring Boot 配置类,控制 Filter 顺序
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<AuthFilter> authFilterRegistration() {FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>(new AuthFilter());registration.addUrlPatterns("/*");registration.setOrder(1); // 优先级高,先执行return registration;}@Beanpublic FilterRegistrationBean<TraceFilter> traceFilterRegistration() {FilterRegistrationBean<TraceFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new TraceFilter());registration.addUrlPatterns("/*");registration.setOrder(2); // 优先级中,执行return registration;}@Beanpublic FilterRegistrationBean<LoggingFilter> loggingFilterRegistration() {FilterRegistrationBean<LoggingFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new LoggingFilter());registration.addUrlPatterns("/*");registration.setOrder(3); // 优先级较低,后执行return registration;}
}
step3.定义测试controller
定义一个controller,用于测试Filter的效果:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ServletFilterController {@GetMapping("ServletFilterController/hello")public String hello() {return "hello world";}
}
step4. 测试
✅ 正常请求(带 Authorization)
- 请求头:
Authorization: valid-token
- 预期输出:
[LoggingFilter] Request started: /MdcTestController/hello
[LoggingFilter] Request traceId: 15b4852b-187b-42e0-a09b-98a34f17ae9b
[LoggingFilter] Request completed in 14 ms
❌ 未授权请求(无 Authorization)
- 预期响应码:
401 Unauthorized
- trace和日志拦截器不会执行,不会有输出日志(因为请求在 AuthFilter 中被拦截)
- 【备注】为方便演示,我们在AuthFilter里将鉴权失败逻辑中的代码改为输出日志了,所以未授权请求输出结果如下所示:
3.3 注意事项
- 使用
FilterRegistrationBean
可以精确控制 Filter 的执行顺序,避免 Spring Boot 自动排序带来的不确定性。 - 若 Filter 不需要作为 Spring Bean 管理,也可使用
@WebFilter
+@Order
,但不如FilterRegistrationBean
灵活。-
import javax.servlet.annotation.WebFilter;@WebFilter("/*") // 拦截所有请求 public class LoggingFilter implements Filter {// ...(同上) }
-
- 所有 Filter 默认对
/*
生效,可根据业务需求限定 URL 模式。
通过组合使用多个 Filter(身份认证 等),我们可以在 Spring Boot 中实现通用、可复用的前置/后置处理逻辑。借助 FilterRegistrationBean
,我们可以清晰地控制 Filter 的执行顺序,从而确保业务逻辑的正确性与稳定性。