拦截器与过滤器
过滤器(Filter)与拦截器(HandlerInterceptor)都做「请求前后处理」,但层级不同:Filter 属于 Servlet 容器层,作用于进入容器的请求;Interceptor 属于 Spring MVC 层,作用于被 DispatcherServlet 分发的请求,能拿到 HandlerMethod 等 MVC 信息。
过滤器注册
写 Filter 实现类(不加 @Component,避免与注册 Bean 重复注册),推荐继承 OncePerRequestFilter 保证一次请求只执行一次:
public class AccessLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
long start = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
System.out.println(request.getRequestURI() + " 耗时 "
+ (System.currentTimeMillis() - start) + "ms");
}
}
}
用 FilterRegistrationBean 注册并控制 URL 与顺序:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<AccessLogFilter> accessLogFilter() {
FilterRegistrationBean<AccessLogFilter> reg = new FilterRegistrationBean<>();
reg.setFilter(new AccessLogFilter());
reg.addUrlPatterns("/*");
reg.setOrder(1); // order 越小越先执行
return reg;
}
}
另一种方式是 @WebFilter + 主类加 @ServletComponentScan(以官方文档为准)。注意:Filter 直接标 @Component 也会被自动注册到所有 URL,再配 FilterRegistrationBean 会重复执行,二选一。
拦截器注册
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) { // 只关心控制器方法
Object user = request.getSession().getAttribute("loginUser");
if (user == null) {
response.sendRedirect("/login");
return false; // 返回 false 中断后续处理
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 请求结束后执行:清理 ThreadLocal、记录异常等
}
}
通过 WebMvcConfigurer 注册并配置拦截范围:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**") // 拦截路径
.excludePathPatterns("/api/login", "/static/**"); // 放行路径
}
}
三个回调方法:preHandle(进入处理器前,返回 false 中断)、postHandle(处理器执行后、视图渲染前)、afterCompletion(整个请求完成后,finally 风格)。拦截器需要依赖其他 Bean 时,把它声明为 @Component 并在配置类里注入使用。
场景对照
| 需求 | 选择 |
|---|---|
| 请求日志、字符编码、容器级通用处理 | Filter |
| 登录鉴权、权限判断(能拿到 HandlerMethod) | Interceptor |
| 接口耗时统计、用户上下文 ThreadLocal 清理 | Interceptor |
小结
先判断处理对象属于「容器请求」还是「控制器方法」:前者用 Filter,后者用 Interceptor。注册套路分别是 FilterRegistrationBean 与 addInterceptors(...).addPathPatterns(...)。