Spring MVC 拦截器:HandlerInterceptor 接口


在 Spring MVC 中,拦截器可以在控制器的执行过程中进行预处理和后处理操作,从而对请求的处理进行控制和增强。Spring MVC 中提供 HandlerInterceptor 接口用于定义拦截器。

HandlerInterceptor 接口中定义了以下三个方法。

  1. preHandle:在控制器执行之前调用。

    可以在这个方法中进行一些预处理操作,比如身份验证、权限检查等。如果返回值为 true,则继续执行后续的拦截器链或进入控制器方法;如果返回值为 false,则中断请求处理流程,不执行后续的拦截器链和控制器方法。

  2. postHandle:在控制器执行之后、视图渲染之前调用。

    可以在这个方法中进行一些后处理操作,比如修改数据模型、添加额外的数据等。该方法中可以对 ModelAndView 对象进行修改。

  3. 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 javax.servlet.http.HttpServletRequest;
import javax.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 接口的配置类,并在配置类中配置拦截器才能使拦截器生效。以下是一个示例,将拦截器配置为拦截所有请求(/**)。

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 javax.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 请求方式访问控制器方法,控制台输出的结果为:

在这里插入图片描述

多个拦截器的实现

定义拦截器

定义多个拦截器,如下例中定义 3 个拦截器。

第一个拦截器:

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 javax.servlet.http.HttpServletRequest;
import javax.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方法执行");
    }

}

第二个拦截器:

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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class MyInterceptor2 implements HandlerInterceptor {

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

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

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

}

第三个拦截器:

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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class MyInterceptor3 implements HandlerInterceptor {

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

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

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

}

配置拦截器

为上述 3 个拦截器进行配置,指定每个拦截器拦截指定的路径。

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 javax.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";
    }

}

通过 HTTP 请求方式访问映射路径为 /test2 的控制器方法,控制台输出的结果为:

在这里插入图片描述

说明访问指定路径(/test2)时,只会生效指定的拦截器(MyInterceptor2)。

多个拦截器的执行顺序

如果配置了多个拦截器,它们之间的执行顺序是什么样的?

修改上例拦截器配置,指定将多个拦截器拦截同一个路径:

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 javax.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("/test2");
        registry.addInterceptor(myInterceptor2)
                .addPathPatterns("/test2");
        registry.addInterceptor(myInterceptor3)
                .addPathPatterns("/test2");
    }

}

输出的结果为:

在这里插入图片描述

说明在定义多个拦截器拦截同一个路径时,拦截器的 preHandle 方法是按照拦截器的定义顺序执行,而 postHandleafterCompletion 方法则是按照拦截器的定义顺序倒序执行。

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

如果请求被某个拦截器拦截,即在某个拦截器中被拒绝放行,那么会出现什么情况?

继续对上例中的 MyInterceptor2 拦截器进行修改,将 preHandle 方法的返回值修改为 false,使请求在 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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class MyInterceptor2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 预处理逻辑,如权限检查
        log.info("【我的拦截器2】preHandle方法执行");
        return false;
    }

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

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

}

通过 HTTP 请求访问 /test2,输出的结果为:

在这里插入图片描述

上述结果说明在定义了多个拦截器的情况下:

  • 请求被任何一个拦截器拦截,目标控制器方法都不会执行;
  • 对于拦截请求的拦截器之前的拦截器,它们的 preHandle 方法将会执行,afterCompletion 方法也会执行;
  • 对于拦截请求的拦截器,它自身的 preHandle 方法将会执行,但是 afterCompletion 方法不会执行;
  • 对于拦截请求的拦截器之后的拦截器,所有的方法都不会执行;
  • 对于所有的拦截器,它们的 postHandle 方法都不会执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JiaHao汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值