什么是 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(),请求流程终止。