Filter 是 Java Servlet 技术中的一个组件,位于客户端请求和目标 Servlet(或 JSP、静态资源)之间。它们能够拦截请求和响应,对其进行预处理和后处理,从而实现功能的扩展和增强。
Filter 作用
Filter 可以在请求到达目标 Servlet 之前或之后拦截请求,并对请求进行处理或修改。在响应返回给客户端之前或之后,Filter 也可以拦截响应,并对响应进行处理或修改,和 Node.js 的 Koa web framework 的中间件机制及其洋葱模型设计极其类似
Filter 在身份验证授权、缓存控制、日志、数据压缩解密、异常处理、请求转发跳转等场景被高频使用
Filter 工作原理
Filter 的工作流程可以简单描述为:
- 请求拦截:当客户端发送请求到服务器时,Servlet 容器会根据配置的 URL 模式,按顺序调用匹配的 Filters。
- 预处理:Filter 对请求进行预处理,如修改请求参数、进行身份验证等。
- 链式调用 :Filter 调用
chain.doFilter(request, response)
以将请求传递给下一个 Filter 或目标 Servlet。 - 后处理:当目标 Servlet 处理完请求并生成响应后,Filter 可以对响应进行后处理,如修改响应内容、添加响应头等。
- 响应返回:最终,处理后的响应返回给客户端。
Filter 的执行顺序由配置方式决定,无论是通过注解还是 web.xml
,都可以指定多个 Filter,并控制其执行顺序。
创建和配置 Filter
Filter 的使用包括实现 Filter 接口、配置 Filter 拦截请求两步
实现 Filter 接口
创建 Filter 需要创建一个 Java 类,并实现 javax.servlet.Filter
接口。该接口包含三个方法:
init()
: 初始化方法,在 Filter 被实例化后调用一次。doFilter()
: 过滤逻辑方法,每次请求都会调用。destroy()
: 销毁方法,在 Filter 被卸载前调用一次,用于资源释放。
java
package com.example;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*") // 通过注解配置 Filter,拦截所有请求
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化代码(如果需要)
System.out.println("LoggingFilter 初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 预处理逻辑
System.out.println("LoggingFilter: 请求开始处理");
// 将请求传递给下一个 Filter 或目标 Servlet
chain.doFilter(request, response);
// 后处理逻辑
System.out.println("LoggingFilter: 请求处理结束");
}
@Override
public void destroy() {
// 清理代码(如果需要)
System.out.println("LoggingFilter 销毁");
}
}
chain.doFilter(request, response);
是不是有 Koa await next()
的味儿
配置 Filter
使用注解
如上例所示,可以通过 @WebFilter
注解来配置 Filter 的拦截 URL 模式和其它属性,是个简单的场景
- urlPatterns 或 value: 指定拦截的 URL 模式。
- filterName: 过滤器名称。
- dispatcherTypes: 指定过滤器应用的调度类型(REQUEST、FORWARD、INCLUDE、ERROR、ASYNC)。
- initParams: 初始化参数。
java
@WebFilter(
filterName = "AuthenticationFilter",
urlPatterns = {"/secure/*"},
dispatcherTypes = {DispatcherType.REQUEST}
)
public class AuthenticationFilter implements Filter {
// 实现 Filter 方法
}
在复杂的 Web 应用中存在多个 Filter,@WebFilter 注解本身不支持 order 属性,无法直接通过注解控制过滤器的执行顺序。在 Spring 中可以使用 @Order
注解或实现 Ordered
接口来指定过滤器的执行顺序。较低的 @Order
值具有较高的优先级,会先执行。
java
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Component
@Order(1) // 优先级最高
@WebFilter("/*")
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤逻辑
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理逻辑
}
}
@Component
@Order(2) // 次优先级
@WebFilter("/secure/*")
public class AuthenticationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化逻辑
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 过滤逻辑
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理逻辑
}
}
如果需求变更,需要中间插入一个 Filter 会导致后面的所有 Filter order 发生变化,如果 Filter 的执行顺序非常重要,最好使用 web.xml 文件来配置 Filter,以确保 Filter 的执行顺序是可靠的
使用 web.xml
在 web.xml 文件中,可以通过 <filter>
和 <filter-mapping>
标签来配置 Filter,web.xml 提供了更多的配置选项,适合复杂的应用场景。在 web.xml 中,Filter 的执行顺序由 <filter-mapping>
在文件中的顺序决定。前面的 Filter 会先执行
xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 定义 Filter -->
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>com.example.LoggingFilter</filter-class>
</filter>
<!-- 配置 Filter 拦截的 URL 模式 -->
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义另一个 Filter -->
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.example.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/secure/*</url-pattern>
</filter-mapping>
</web-app>
Filter 链
Filter 可以串联形成一个 Filter 链 (Filter Chain) ,多个 Filter 按照配置的顺序依次拦截和处理请求。每个 Filter 都有机会对请求和响应进行修改或处理,过程和 Koa 的洋葱模型非常类似
- 请求到达 Filter 链的第一个 Filter
- 第一个 Filter 执行预处理
- 调用
chain.doFilter(request, response)
:- 请求继续传递到下一个 Filter
- 或者最终传递到目标 Servlet
- Filter 执行后处理(响应返回时)
- 响应传递回上一个 Filter,以此类推,直到响应返回给客户端
plain
请求 → Filter1 → Filter2 → Servlet → Filter2(后处理) → Filter1(后处理) → 响应
一个简单的日志记录 Filter
使用 Filter 记录每个 HTTP 请求的详细信息
java
package org.example;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
@WebFilter("/*") // 拦截所有请求
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化日志记录器(如果需要)
System.out.println("LoggingFilter 初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 转换为 HttpServletRequest 以获取更多信息
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 获取请求信息
String method = httpRequest.getMethod();
String uri = httpRequest.getRequestURI();
String clientIP = request.getRemoteAddr();
Date requestTime = new Date();
// 记录日志
System.out.printf("[%s] %s request for %s from %s%n", requestTime, method, uri, clientIP);
// 继续处理请求
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理资源(如果需要)
System.out.println("LoggingFilter 销毁");
}
}