什么是 Filter?为什么它如此重要?
Filter
(过滤器)是 Java Servlet 规范中定义的一个接口,用于在请求到达 Servlet
之前或响应返回客户端之前,对请求和响应进行预处理或后处理。
📌 核心能力:
- 统一设置字符编码
- 记录请求日志
- 实现权限校验
- 处理跨域(CORS)
- 压缩响应内容
- 修改请求/响应体(需包装)
✅ Filter 是实现“横切关注点(Cross-Cutting Concerns)”的关键机制。
1️⃣ Filter 的执行流程
当一个请求到达 Web 容器(如 Tomcat)时,执行流程如下:
+------------+| HTTP 请求 |+-----+------+|v+-------------+ +-------------+ +-----------+| Filter 1 | --> | Filter 2 | --> | Servlet |+------+------+ +------+------+ +-----+-----+| | |v v v预处理请求/响应 预处理请求/响应 业务逻辑处理↑ ↑| |+-------------+ | +------------------+| 响应后处理 |<------+ | 返回响应 |+-------------+ +------------------+
2️⃣ FilterChain.doFilter()
的魔法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 1. 请求预处理(Pre-processing)System.out.println("Filter1: 请求开始...");// 2. 放行,交给下一个 Filter 或 Servletchain.doFilter(request, response);// 3. 响应后处理(Post-processing)System.out.println("Filter1: 请求结束...");
}
📌 关键点:
chain.doFilter()
不是“结束”,而是“放行”- 调用后,控制权交给后续组件
- 后续执行完毕后,控制权会回到当前 Filter,继续执行
doFilter()
之后的代码 - 这正是 责任链模式 的体现:每个 Filter 只处理自己的逻辑,然后将责任传递下去
实战:开发经典 Filter 组件
场景 1:统一字符编码 Filter
public class EncodingFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 设置请求和响应编码request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");// 放行chain.doFilter(request, response);}
}
场景 2:请求日志记录 Filter
public class LoggingFilter implements Filter {private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;long startTime = System.currentTimeMillis();logger.info("请求开始: {} {}", req.getMethod(), req.getRequestURI());// 放行chain.doFilter(request, response);// 响应后处理long duration = System.currentTimeMillis() - startTime;logger.info("请求结束: {} {} - 耗时: {}ms", req.getMethod(), req.getRequestURI(), duration);}
}
场景 3:权限校验 Filter
public class AuthFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;String token = req.getHeader("Authorization");if (token == null || !isValidToken(token)) {resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);resp.getWriter().write("{\"error\": \"未授权\"}");return; // ❌ 阻止放行!}// ✅ 校验通过,放行chain.doFilter(request, response);}private boolean isValidToken(String token) {// 简化校验逻辑return "Bearer valid-token-123".equals(token);}
}
📌 关键:如果校验失败,不调用 chain.doFilter()
,请求流程终止。