Spring MVC 拦截器 (HandlerInterceptor) 是什么? 它与 Servlet Filter 有什么区别?

Spring MVC 拦截器 (HandlerInterceptor) 是 Spring Web MVC 框架提供的一种机制,在请求处理的特定阶段插入自定义逻辑。它主要用于预处理和后处理 Controller (Handler) 的请求。

Spring MVC 拦截器 (HandlerInterceptor) 是什么?

HandlerInterceptor 是一个接口,它定义了三个主要的回调方法,在请求生命周期的不同点进行干预:

  1. preHandle(HttpServletRequest request, HttpServletResponse response, Object handler):

    • 调用时机: 在 Controller 方法 (Handler) 执行之前调用。
    • 返回值:
      • true: 请求会继续向下执行,传递给下一个拦截器或最终的 Handler。
      • false: 请求处理流程被中断,不会执行后续的拦截器和 Handler。此时,需要自己负责生成响应 (例如,通过 response.getWriter().write(...))。
    • 用途: 权限校验、日志记录、参数预处理、请求合法性检查等。
  2. postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView):

    • 调用时机: 在 Controller 方法 (Handler) 执行之后,但在 DispatcherServlet 渲染视图之前调用。
    • 参数 modelAndView 包含了 Handler 返回的模型数据和视图信息。我们可以在这里修改 ModelAndView
    • 注意: 如果 Handler 方法执行过程中抛出异常,则此方法不会被调用。
    • 用途: 修改模型数据、添加公共数据到模型、修改视图名称、记录操作日志(此时可以获取到Handler处理结果的一些信息)。
  3. afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex):

    • 调用时机: 在整个请求处理完成之后,即 DispatcherServlet 渲染视图之后调用。通常在视图渲染完毕后执行,用于资源清理。
    • 参数 ex 如果 Handler 执行过程中或视图渲染过程中抛出了异常,ex 会包含该异常对象,否则为 null
    • 注意: 无论 Handler 方法是否成功执行或是否抛出异常,此方法总是 会被调用 (前提是 preHandle 返回 true)。
    • 用途: 资源清理、异常日志记录、性能监控(记录整个请求处理时间)。

HandlerInterceptor 与 Servlet Filter 的区别:

特性 Servlet Filter (javax.servlet.Filter) Spring MVC HandlerInterceptor (org.springframework.web.servlet.HandlerInterceptor)
作用范围 基于 Servlet 规范,作用于 Servlet 容器级别。可以拦截所有进入 Servlet 的请求,不限于 Spring MVC。 仅作用于 Spring MVC 的 DispatcherServlet 处理的请求。它位于 DispatcherServlet 内部的请求处理链中。
依赖 依赖 Servlet API。 依赖 Spring MVC 框架。
调用时机 DispatcherServlet 之前(或之后,取决于 Filter Chain 的配置顺序)。 DispatcherServlet 接收到请求后,但在调用具体的 Controller (Handler) 之前、之后和完成时。
可获取信息 只能获取到原始的 HttpServletRequestHttpServletResponse 可以获取到 Spring MVC 上下文中的信息,例如将要执行的 Handler (Controller 方法)、ModelAndView 等。
控制粒度 相对粗粒度,通常用于全局性的任务,如编码转换、安全过滤。 更加细粒度,可以针对特定的 Handler 或 URL 模式进行拦截,能更深入地参与到 Spring MVC 的请求处理流程中。
异常处理 Filter 的 doFilter 方法需要自己处理异常,或向上抛出。 postHandle 不会在 Handler 抛异常时调用,但 afterCompletion 始终会被调用,并可以获取到异常信息。
与Spring集成 可以通过 FilterRegistrationBean 在 Spring Boot 中注册,但本质上仍是 Servlet 规范的一部分。 是 Spring MVC 的核心组件,与 Spring IoC 容器紧密集成,可以方便地注入其他 Spring Bean。

简单来说:

  • Filter 更底层,更通用,作用范围更广,适合做一些与具体框架无关的通用处理。
  • Interceptor 更贴近 Spring MVC,更精细,能访问到更多 Spring MVC 的内部信息,适合做与 Spring MVC 请求处理流程相关的特定逻辑。

如何定义和注册拦截器?

  1. 定义拦截器 (实现 HandlerInterceptor 接口或继承 HandlerInterceptorAdapter (已废弃,推荐直接实现接口)):

    java 复制代码
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Component // 将拦截器注册为一个Bean,如果需要在拦截器中注入其他Bean
    public class MyCustomInterceptor implements HandlerInterceptor {
    
        private static final Logger logger = LoggerFactory.getLogger(MyCustomInterceptor.class);
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            logger.info("MyCustomInterceptor: PreHandle - URL: {}", request.getRequestURI());
            // 示例:简单的权限校验
            String token = request.getHeader("Authorization");
            if (token == null || !isValidToken(token)) {
                logger.warn("MyCustomInterceptor: PreHandle - Unauthorized access to {}", request.getRequestURI());
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("Unauthorized - Invalid or missing token.");
                return false; // 中断请求
            }
            return true; // 继续执行
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            logger.info("MyCustomInterceptor: PostHandle - Handler: {}", handler.getClass().getSimpleName());
            if (modelAndView != null) {
                modelAndView.addObject("interceptorMessage", "Message added by interceptor!");
                logger.info("MyCustomInterceptor: PostHandle - Added data to ModelAndView for view: {}", modelAndView.getViewName());
            }
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            logger.info("MyCustomInterceptor: AfterCompletion - Request completed.");
            if (ex != null) {
                logger.error("MyCustomInterceptor: AfterCompletion - Exception occurred: ", ex);
            }
        }
    
        private boolean isValidToken(String token) {
            // 实际的token校验逻辑
            return token.startsWith("Bearer valid-");
        }
    }
  2. 注册拦截器 (通过实现 WebMvcConfigurer 接口):

    这是在 Spring Boot 和现代 Spring MVC Java 配置中最常见的方式。

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Autowired
        private MyCustomInterceptor myCustomInterceptor; // 注入自定义拦截器Bean
    
        // 如果你的拦截器没有被@Component注解,或者你想在这里new一个实例,也可以:
        // private final MyCustomInterceptor myCustomInterceptor = new MyCustomInterceptor();
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(myCustomInterceptor)
                    .addPathPatterns("/api/**", "/secure-area/**") // 指定拦截的URL模式 (Ant-style)
                    .excludePathPatterns("/api/public/**", "/login", "/error") // 指定排除的URL模式
                    .order(1); // 可选,指定拦截器的执行顺序,数字越小优先级越高
    
            // 可以注册多个拦截器
            // registry.addInterceptor(new AnotherInterceptor())
            //         .addPathPatterns("/admin/**")
            //         .order(0);
        }
    }

    注意:

    • addPathPatterns("/**"): 拦截所有请求。
    • excludePathPatterns(...): 排除某些路径不被拦截。
    • order(int order): 如果有多个拦截器,可以通过 order 来指定它们的执行顺序。preHandle 会按顺序执行,postHandleafterCompletion 会按相反的顺序执行。

拦截器可以用来做什么?

拦截器因其能够在请求处理的关键点介入,具有广泛的应用场景:

  1. 权限认证与授权:

    • preHandle 中检查用户是否登录,是否有访问特定资源的权限。如果无权限,则返回 false 并设置相应的响应状态码或重定向到登录页面。
  2. 日志记录:

    • preHandle: 记录请求的URL、参数、来源IP等。
    • postHandle: 记录Handler处理结果、返回的视图等。
    • afterCompletion: 记录整个请求的处理耗时、异常信息等。
  3. 性能监控:

    • preHandle 中记录开始时间,在 afterCompletion 中计算总耗时。
  4. 统一添加数据到模型:

    • postHandle 中向 ModelAndView 添加所有页面都需要用到的公共数据,如网站名称、当前用户信息等。
  5. 国际化与本地化:

    • 根据请求参数或Session信息,在 preHandle 中设置当前的 Locale
  6. 防止重复提交:

    • preHandle 中生成并校验Token,防止用户重复提交表单。
  7. 请求参数预处理或校验:

    • preHandle 中对请求参数进行一些预处理或基础校验。
  8. 请求上下文设置/清理:

    • 使用 ThreadLocalpreHandle 中设置一些请求相关的上下文信息,在 afterCompletion 中清理。
  9. API版本控制:

    • 根据请求头或路径信息,在 preHandle 中将请求路由到不同版本的API处理器。

Spring MVC 拦截器提供了灵活的方式来对Web请求进行横切点的处理,使得Controller可以更专注于核心业务逻辑。

相关推荐
饕餮争锋1 小时前
javax.servlet.Filter 介绍-笔记
笔记·servlet
努力学习的明1 小时前
Spring MVC 对 JavaWeb 的优化:从核心组件到注解
java·spring·mvc·web
木梓辛铭3 小时前
Spring Cache的详细使用
java·后端·spring
饕餮争锋3 小时前
WebMvcConfigurer介绍-笔记
java·笔记·servlet
长勺5 小时前
Spring Security vs Shiro vs Sa-Token
java·后端·spring
xlsw_8 小时前
mvc-service引入
mvc
Volunteer Technology9 小时前
SpringCloud Gateway知识点整理和全局过滤器实现
spring·spring cloud·gateway
wxin_VXbishe17 小时前
springboot旅游小程序-计算机毕业设计源码76696
java·spring boot·python·spring·django·sqlite·flask
残花月伴17 小时前
springCloud/Alibaba常用中间件之Setinel实现熔断降级
spring·spring cloud·中间件