SpringMVC HandlerInterceptor拦截器接口解读

作者:技术拾光时间:2026-06-10 08:01:01浏览:552

HandlerInterceptor是SpringMVC拦截机制核心,定义preHandle、postHandle、afterCompletion方法。preHandle预处理,返回false则拦截;postHandle在控制器执行后、视图渲染前调用;afterCompletion视图渲染后执行。多拦截器时,preHandle顺序执行,postHandle和

HandlerInterceptor

HandlerInterceptor 是 Spring MVC 拦截机制的核心接口,它定义了三个关键方法,像三道关卡一样,贯穿请求处理的整个生命周期。

preHandle:这个方法在控制器方法执行之前被调用。你可以把它想象成一个“安检门”,非常适合做身份验证、权限检查这类预处理工作。它的返回值决定了请求是否能继续前行:返回 true,说明通过检查,请求会被放行,接着进入下一个拦截器或控制器;返回 false,则请求在此被拦截,后续的拦截器和控制器都不会再执行。

postHandle:它在控制器方法执行完毕、但视图还没有渲染之前被调用。这是一个“后加工车间”,你可以在这里对返回的 ModelAndView 对象动手脚,比如往模型里加一些公共数据,或者对视图名称进行调整。

afterCompletion:这是最后一步,在视图渲染完成之后才触发。它更像一个“清道夫”,主要用于释放资源、记录日志,或者统一处理 Exception 异常信息,非常适合做一些收尾工作。

单个拦截器的实现

接下来,我们通过一个完整的例子,看看单个拦截器是如何落地实现的。

定义拦截器

首先,实现 HandlerInterceptor 接口来定义一个拦截器类:

package test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 预处理逻辑,如权限检查
        log.info("preHandle方法执行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 后处理逻辑,可以修改数据模型
        log.info("postHandle方法执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 资源清理逻辑,比如释放资源或记录日志
        log.info("afterCompletion方法执行");
    }

}

配置拦截器

定义好拦截器后,需要告诉 Spring MVC 哪些请求应该被它拦截。这需要创建一个实现了 WebMvcConfigurer 接口的配置类,并在 addInterceptors 方法中进行注册。下面的例子将拦截器配置为拦截所有请求(即 /** 路径):

package test;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import ja vax.annotation.Resource;

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

    @Resource
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**");
    }

}

测试拦截器

最后,准备一个简单的控制器来验证拦截器是否生效:

package test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class MyController {

    @GetMapping("/test")
    public String test() {
        log.info("控制器方法执行");
        return "OK";
    }
    
}

启动项目,通过 HTTP 请求访问 /test 路径,控制台会输出如下结果:

SpringMVC HandlerInterceptor拦截器接口解读

从输出顺序可以很清晰地看到拦截器的执行流程:preHandle 先执行,然后才是控制器方法,接着是 postHandle,最后是 afterCompletion。

多个拦截器的实现

实际项目中,我们通常会配置不止一个拦截器来处理不同维度的逻辑。那多个拦截器之间是如何协作的呢?我们来一步步拆解。

定义拦截器

先定义三个拦截器,分别命名为 MyInterceptor1、MyInterceptor2 和 MyInterceptor3。它们的代码结构相同,只是日志输出不同,以便区分执行顺序。这里以第一个为例展示:

package test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class MyInterceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("【我的拦截器1】preHandle方法执行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("【我的拦截器1】postHandle方法执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("【我的拦截器1】afterCompletion方法执行");
    }

}

配置拦截器

接下来配置这些拦截器,让它们分别拦截不同的路径:/test1、/test2 和 /test3。

package test;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import ja vax.annotation.Resource;

@Configuration
public class MyMultipleInterceptorConfig implements WebMvcConfigurer {

    @Resource
    private MyInterceptor1 myInterceptor1;

    @Resource
    private MyInterceptor2 myInterceptor2;

    @Resource
    private MyInterceptor3 myInterceptor3;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor1)
                .addPathPatterns("/test1");
        registry.addInterceptor(myInterceptor2)
                .addPathPatterns("/test2");
        registry.addInterceptor(myInterceptor3)
                .addPathPatterns("/test3");
    }

}

测试拦截器

在控制器中为这些路径添加对应的处理方法:

package test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class MyMultipleInterceptorTestController {

    @GetMapping("/test1")
    public String test1() {
        log.info("控制器test1方法执行");
        return "OK";
    }

    @GetMapping("/test2")
    public String test2() {
        log.info("控制器test2方法执行");
        return "OK";
    }

    @GetMapping("/test3")
    public String test3() {
        log.info("控制器test3方法执行");
        return "OK";
    }

}

现在,如果访问 /test2 这个路径,控制台输出的结果会是:

SpringMVC HandlerInterceptor拦截器接口解读

这清楚地表明,访问某个路径时,只会触发专门为该路径配置的拦截器。这是一种精确的拦截控制。

多个拦截器的执行顺序

如果把多个拦截器配置为拦截同一个路径,结果又如何呢?我们修改配置,让三个拦截器都拦截 /test2 路径:

// ... 配置代码中,将三个拦截器的 addPathPatterns 都改为 "/test2" ...

再次访问 /test2,输出结果如下:

SpringMVC HandlerInterceptor拦截器接口解读

这里有个关键点需要注意:preHandle 方法的执行顺序与我们注册拦截器的顺序一致(1 -> 2 -> 3),但 postHandle 和 afterCompletion 方法的执行顺序却完全相反,变成了 3 -> 2 -> 1。这种“先进后出”的栈式结构,在日志处理和资源清理场景中非常常见。

某个拦截器拒绝放行的情况

如果请求在某个拦截器中被拦截(即 preHandle 返回 false),又会发生什么?我们将 MyInterceptor2 的 preHandle 返回值改为 false:

// MyInterceptor2 的 preHandle 方法中,return false;

再次访问 /test2,输出结果如下:

SpringMVC HandlerInterceptor拦截器接口解读

从结果可以总结出几条重要规则:

  • 只要任何一个拦截器的 preHandle 返回 false,目标控制器方法就绝对不会执行。
  • 对于拦截器链中,位于“拒绝拦截器”之前的拦截器(本例中的 MyInterceptor1),它们的 preHandle 和 afterCompletion 方法都会执行。
  • 对于那个“拒绝拦截器”本身(MyInterceptor2),它的 preHandle 会执行,但 afterCompletion 不会执行。
  • 对于“拒绝拦截器”之后的所有拦截器(MyInterceptor3),它们的方法都不会被执行。
  • 所有拦截器的 postHandle 方法都不会执行,因为控制器方法根本没被调用。

理解这个执行模型,对于调试复杂的拦截器链问题会非常有帮助。

总结

HandlerInterceptor 的三个方法形成了请求处理的前、中、后三个阶段,为我们在 Spring MVC 中实现权限控制、日志记录、数据预处理等提供了强大的手段。理解单个拦截器的配置和多个拦截器之间的执行顺序,尤其是在异常情况下的行为,是掌握 Spring MVC 拦截器机制的关键所在。