一、过滤器基础概念
-
什么是过滤器?
- 过滤器(Filter)是Java Web开发中的核心组件,位于客户端与服务器资源之间,能够对HTTP请求和响应进行预处理和后处理
- 类比安检门:所有进入(请求)和离开(响应)应用的数据都要经过过滤器的检查和处理
-
过滤器核心特性
- 请求拦截:可拦截进入应用的HTTP请求
- 双向处理:既能处理请求(Request),也能处理响应(Response)
- 链式调用:多个过滤器可形成处理链
- 生命周期管理:包含初始化(init)、执行(doFilter)和销毁(destroy)三个阶段
-
过滤器与拦截器区别
- 归属不同:过滤器是Servlet规范的一部分,拦截器是Spring MVC组件
- 执行时机:过滤器在Servlet之前执行,拦截器在DispatcherServlet之后执行
- 依赖关系:过滤器不依赖Spring容器,拦截器依赖Spring容器
二、SpringBoot中实现过滤器的三种方式
-
方式1:使用@Component注解(最简单)
less@Component @WebFilter(urlPatterns = "/*") public class SimpleFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("【简单过滤器】请求进入"); chain.doFilter(request, response); System.out.println("【简单过滤器】响应返回"); } }
- 需在启动类添加
@ServletComponentScan
- 需在启动类添加
-
方式2:使用FilterRegistrationBean(更灵活)
typescript@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<TimeCostFilter> timeCostFilter() { FilterRegistrationBean<TimeCostFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new TimeCostFilter()); registration.addUrlPatterns("/*"); registration.setOrder(1); // 设置执行顺序 return registration; } }
- 可精确控制URL匹配、执行顺序等
-
方式3:实现Filter接口并手动注册(最原始)
- 传统Servlet配置方式,适用于非Spring环境
三、过滤器生命周期与执行顺序
-
生命周期详解
init()
:容器启动时调用,适合加载资源、初始化配置doFilter()
:每次请求都会调用,核心处理方法destroy()
:容器关闭时调用,适合释放资源
-
执行顺序控制
-
通过
FilterRegistrationBean.setOrder()
或@Order
注解控制 -
规则:
- order值越小,优先级越高
- 相同order值时,按bean注册顺序执行
- 正向流程按order升序执行,逆向流程按order降序执行
-
-
多过滤器配置示例
typescript@Configuration public class MultiFilterConfig { @Bean public FilterRegistrationBean<FirstFilter> firstFilter() { FilterRegistrationBean<FirstFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new FirstFilter()); registration.addUrlPatterns("/*"); registration.setOrder(1); // 最高优先级 return registration; } @Bean public FilterRegistrationBean<SecondFilter> secondFilter() { registration.setFilter(new SecondFilter()); registration.setOrder(2); // 次高优先级 return registration; } }
四、过滤器高级应用场景
-
权限验证过滤器
inipublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String token = httpRequest.getHeader("Authorization"); if(!validateToken(token)) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); httpResponse.getWriter().write("Unauthorized"); return; } chain.doFilter(request, response); }
- 检查Token或Session,阻止未授权访问
-
请求日志记录过滤器
vbscriptpublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; log.info("Request: {} {}, Params: {}", httpRequest.getMethod(), httpRequest.getRequestURI(), httpRequest.getQueryString()); ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(httpRequest); chain.doFilter(wrappedRequest, response); HttpServletResponse httpResponse = (HttpServletResponse) response; log.info("Response Status: {}", httpResponse.getStatus()); }
- 记录请求和响应信息用于监控和调试
-
跨域处理过滤器
vbscriptpublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); chain.doFilter(req, res); }
- 解决前端跨域请求问题
-
性能监控过滤器
inipublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { long startTime = System.currentTimeMillis(); try { chain.doFilter(request, response); } finally { long duration = System.currentTimeMillis() - startTime; HttpServletRequest httpRequest = (HttpServletRequest) request; System.out.printf("请求 %s %s 耗时 %d ms%n", httpRequest.getMethod(), httpRequest.getRequestURI(), duration); } }
- 统计请求处理时间,用于性能优化
五、常见问题与解决方案
-
过滤器执行顺序问题
- 问题场景:多个过滤器需要按特定顺序执行
- 解决方案:明确设置每个过滤器的order值,使用FilterRegistrationBean注册时指定顺序
-
请求body多次读取问题
- 问题场景:流只能读取一次,后续读取会为空
- 解决方案:使用请求包装类
ContentCachingRequestWrapper
vbscriptpublic class RepeatReadFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request); chain.doFilter(requestWrapper, response); } }
-
在过滤器中获取Spring Bean
-
默认情况下,过滤器不是由Spring管理的,无法直接使用@Autowired
-
解决方案1:使用SpringBeanAutowiringSupport
javapublic class SpringAwareFilter implements Filter { @Autowired private UserService userService; @Override public void init(FilterConfig filterConfig) throws ServletException { SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext()); } }
-
解决方案2:通过FilterRegistrationBean注入
typescript@Configuration public class FilterConfig { @Autowired private UserService userService; @Bean public FilterRegistrationBean<SpringAwareFilter> springAwareFilter() { FilterRegistrationBean<SpringAwareFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new SpringAwareFilter(userService)); return registration; } }
-
六、项目实战:构建API网关过滤器
java
public class ApiGatewayFilter implements Filter {
private static final Set<String> WHITE_LIST = Set.of("/api/auth/login", "/api/auth/register");
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 1. 包装请求和响应以便多次读取
ContentCachingRequestWrapper requestWrapper =
new ContentCachingRequestWrapper(httpRequest);
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper(httpResponse);
// 2. 记录请求开始时间
long startTime = System.currentTimeMillis();
try {
// 3. 跨域处理
handleCors(httpRequest, httpResponse);
// 4. 白名单检查
if (isWhiteList(httpRequest.getRequestURI())) {
chain.doFilter(requestWrapper, responseWrapper);
return;
}
// 5. 认证检查
if (!checkAuth(httpRequest)) {
sendErrorResponse(httpResponse, 401, "未授权");
return;
}
// 6. 限流检查
if (!rateLimit(httpRequest)) {
sendErrorResponse(httpResponse, 429, "请求过于频繁");
return;
}
// 7. 继续处理请求
chain.doFilter(requestWrapper, responseWrapper);
} catch (Exception e) {
// 8. 异常处理
handleException(e, httpResponse);
} finally {
// 9. 记录请求日志
logRequest(requestWrapper, responseWrapper, startTime);
// 10. 确保响应被写回客户端
responseWrapper.copyBodyToResponse();
}
}
private boolean isWhiteList(String uri) {
return WHITE_LIST.contains(uri);
}
private boolean checkAuth(HttpServletRequest request) {
String token = request.getHeader("Authorization");
return token != null && token.startsWith("Bearer ");
}
private boolean rateLimit(HttpServletRequest request) {
String ip = request.getRemoteAddr();
// 简单的限流逻辑,实际项目应该使用Redis等实现
return true;
}
private void handleCors(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
}
}
- 该过滤器实现了API网关常见的功能:跨域处理、白名单检查、认证检查、限流等
七、最佳实践与性能优化
-
过滤器命名规范
-
建议采用功能+Filter的命名方式,例如:
- LoggingFilter:日志记录
- AuthFilter:权限验证
- CorsFilter:跨域处理
-
-
过滤器职责单一原则
-
每个过滤器应该只关注一个特定功能
-
不好的做法:
java// 一个过滤器同时处理日志、权限和跨域 public class AllInOneFilter implements Filter { // 不推荐 }
-
好的做法:
java// 分别实现不同的过滤器 public class LoggingFilter implements Filter { ... } public class AuthFilter implements Filter { ... } public class CorsFilter implements Filter { ... }
-
-
过滤器异常处理
vbscriptpublic class ExceptionHandlerFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (BusinessException e) { handleBusinessException((HttpServletResponse) response, e); } catch (Exception e) { handleUnexpectedException((HttpServletResponse) response, e); } } private void handleBusinessException(HttpServletResponse response, BusinessException e) throws IOException { response.setContentType("application/json"); response.setStatus(e.getStatusCode()); response.getWriter().write(e.toJson()); } private void handleUnexpectedException(HttpServletResponse response, Exception e) throws IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.getWriter().write("{"error":"服务器内部错误"}"); log.error("未处理的异常", e); } }
- 在过滤器中妥善处理异常,避免影响主流程
-
性能优化建议
- 避免在过滤器中执行耗时操作
- 合理设置过滤路径,不要过度拦截
- 过滤器是单例的,注意实例变量的线程安全
- 推荐使用局部变量而非成员变量