拦截器与过滤器

过滤器(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。注册套路分别是 FilterRegistrationBeanaddInterceptors(...).addPathPatterns(...)

笔记加载中…