spring MVC 拦截器

一、Spring MVC拦截器的作用

拦截器是Spring MVC框架中处理 HTTP请求 的一种机制,通常用于在请求到达 控制器(Controller) 之前或从控制器返回结果之后进行额外的逻辑处理。可以用于以下场景:

  • 日志记录:记录每次请求的开始时间、结束时间、请求的参数、响应的结果等。
  • 认证和授权:在请求处理之前,检查用户的身份和权限。
  • 统一异常处理:捕获请求处理中的异常,并返回统一的错误响应。
  • 性能监控:记录请求处理的时间。
  • 请求修改:在请求被控制器处理之前,修改请求数据。

拦截器的定义要分为两个部分:

  • 拦截器核心实现 :通过实现 HandlerInterceptor 接口定义拦截器的三大核心方法(preHandlepostHandleafterCompletion),实现请求拦截的核心逻辑。
  • 拦截器注册配置 :通过实现 WebMvcConfigurer 接口的 addInterceptors() 方法,将拦截器注册到应用中,并指定拦截规则(路径、排除路径等)

二、拦截器核心实现

通过实现 HandlerInterceptor 接口,定义具体的拦截逻辑,包括请求预处理、方法后处理以及请求完成后的清理操作。对应的三个方法是:

  1. preHandle() :在请求进入控制器方法之前执行。如果返回 false,则请求不会继续执行控制器方法;如果返回 true,则请求继续执行。
  2. postHandle():在控制器方法执行之后,但在视图渲染之前执行。可以对返回的数据进行修改。
  3. afterCompletion():在整个请求结束(即视图渲染完成)之后执行,通常用于资源清理等操作。

以上三个方法的重写都在实现HandlerInterceptor 接口的类中进行重写,如下面例子中的MyInterceptor类。

示例代码

java 复制代码
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 请求预处理逻辑
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 请求后处理逻辑
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求完成后的清理操作
    }
}

1. preHandle() 方法

方法签名

java 复制代码
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

参数说明

  • HttpServletRequest request:表示当前的HTTP请求对象,通过这个对象可以获取请求的各种信息,如请求的URL、请求头、请求参数、请求体等。
  • HttpServletResponse response:表示当前的HTTP响应对象,可以通过这个对象设置响应的状态码、响应头或直接写出响应数据。
  • Object handler :表示当前要执行的处理器(通常是一个 Controller 方法)。通过这个参数你可以获取到处理这个请求的目标方法和类。

常见参数使用

  • request.getMethod():获取请求方法(GET、POST等)。
  • request.getRequestURI():获取请求的URI,通常用于判断请求路径。
  • response.setStatus() :设置HTTP响应状态码,比如 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED) 设置为未授权状态码。
  • handler.getClass() :通过 handler 对象获取处理当前请求的控制器类。

preHandle() 示例

java 复制代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 获取请求的URI
    String uri = request.getRequestURI();
    System.out.println("Request URI: " + uri);

    // 检查用户是否已登录
    String token = request.getHeader("Authorization");
    if (token == null || !isValidToken(token)) {
        // 如果未登录,返回401未授权状态,并中止请求
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return false;
    }

    // 如果用户已登录,允许继续处理请求
    return true;
}

private boolean isValidToken(String token) {
    // 模拟Token验证逻辑
    return "valid_token".equals(token);
}
  • request.getRequestURI():获取请求的URL路径,用于日志记录或路径匹配。
  • response.setStatus(HttpServletResponse.SC_UNAUTHORIZED):设置未授权响应状态码,当Token无效时直接返回,不继续执行控制器。
  • return true :表示请求可以继续执行,控制器方法将被调用。如果返回 false,请求不会进入控制器。

2. postHandle() 方法

方法签名

java 复制代码
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;

参数说明

  • HttpServletRequest request :与 preHandle() 中的请求对象相同,允许你访问请求中的数据(如参数、头部信息等)。
  • HttpServletResponse response :与 preHandle() 中的响应对象相同,允许你在控制器方法执行完后修改响应(如设置响应头)。
  • Object handler :与 preHandle() 中的 handler 相同,表示处理请求的控制器方法。
  • ModelAndView modelAndView :表示控制器方法返回的视图和数据模型。在视图渲染之前,允许你修改返回的数据或视图。如果控制器方法返回 null,表示没有返回视图。

常见参数使用

  • modelAndView.addObject():可以向返回的模型中添加额外的数据。
  • response.addHeader():可以在响应中添加自定义的头部信息。
  • handler.getClass():获取控制器类的信息,用于日志记录等操作。

postHandle() 示例

java 复制代码
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    // 检查是否有ModelAndView对象
    if (modelAndView != null) {
        // 在模型中添加一个全局信息
        modelAndView.addObject("globalMessage", "This is a global message for all views");
    }

    // 添加自定义响应头
    response.addHeader("X-Custom-Header", "PostHandleExample");

    // 记录处理器的类名和方法
    System.out.println("Handler class: " + handler.getClass().getSimpleName());
}
  • modelAndView.addObject():向返回的视图模型中添加一个全局变量,所有视图中都可以使用这个变量。
  • response.addHeader():在响应中添加一个自定义头部信息。
  • handler.getClass().getSimpleName():获取处理当前请求的控制器类名,用于日志记录或调试。

3. afterCompletion() 方法

方法签名

java 复制代码
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;

参数说明

  • HttpServletRequest request :与 preHandle()postHandle() 中的请求对象相同,允许你在请求完成后访问请求信息。
  • HttpServletResponse response :与 preHandle()postHandle() 中的响应对象相同,允许你在请求完成后对响应做最后的处理。
  • Object handler :与 preHandle()postHandle() 中的 handler 相同,表示处理当前请求的控制器方法。
  • Exception ex :表示请求处理过程中可能抛出的异常。如果控制器方法或其他部分抛出了异常,ex 参数就会包含这个异常信息;如果没有异常,exnull

常见参数使用

  • request.getAttribute() :从请求对象中获取之前设置的属性,比如在 preHandle() 中设置的时间戳,用于计算请求处理时间。
  • ex != null:检查是否有异常,如果有异常则记录异常信息或进行相应处理。
  • response.getStatus():获取响应的状态码,比如检查是否返回了500或其他错误状态码。

afterCompletion() 示例

java 复制代码
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    // 获取请求处理时间
    Long startTime = (Long) request.getAttribute("startTime");
    long endTime = System.currentTimeMillis();
    long executionTime = endTime - startTime;

    // 记录请求的处理时间
    System.out.println("Request URI: " + request.getRequestURI());
    System.out.println("Execution Time: " + executionTime + "ms");

    // 检查是否有异常
    if (ex != null) {
        System.out.println("Request raised exception: " + ex.getMessage());
    }

    // 可以在此处清理资源或执行其他必要操作
}
  • request.getAttribute("startTime") :从请求对象中获取 preHandle() 中设置的开始时间属性,用来计算整个请求的处理时间。
  • ex != null:检查是否有异常,如果请求过程中抛出了异常,记录异常信息。
  • response.getStatus():可以获取HTTP响应的状态码,检查是否返回了错误状态码(如500,404等)。

4.三个方法的执行流程

  1. preHandle() :请求进入控制器之前执行。如果返回 false,请求将被终止,不会继续执行控制器方法;如果返回 true,则继续执行控制器方法。
  2. 控制器方法执行 :如果 preHandle() 返回 true,则会执行控制器方法。
  3. postHandle() :在控制器方法执行完之后,但在视图渲染之前执行。可以在此处修改返回的 ModelAndView 或响应对象。
  4. 视图渲染 :将控制器方法返回的 ModelAndView 渲染为HTML或JSON响应。
  5. afterCompletion():请求处理完成(包括视图渲染)之后执行。用于资源清理、日志记录或处理异常。

Spring BootSpring MVC 中,使用拦截器(Interceptor)是一种常见的方式来在 HTTP请求处理流程 的不同阶段执行额外的逻辑。要在Spring中注册拦截器,我们需要通过 Java配置 ,实现 WebMvcConfigurer 接口的 addInterceptors() 方法。

下面我将详细解释如何在 Spring BootSpring MVC 项目中通过实现 WebMvcConfigurer 接口来注册拦截器,并通过 addInterceptors() 方法来配置拦截器的路径规则。

三、拦截器注册配置

接下来,我们需要将拦截器注册到Spring MVC中。要做到这一点,我们需要创建一个Java配置类,并实现 WebMvcConfigurer 接口。然后在 addInterceptors() 方法中注册我们自定义的拦截器,定义拦截的 URL 路径或排除的路径。

我们需要创建一个带有 @Configuration 注解的配置类,它会在Spring启动时自动加载。

示例:Java配置类

java 复制代码
package com.example.config;

import com.example.interceptor.MyInterceptor;
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 WebConfig implements WebMvcConfigurer {

    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 将自定义拦截器注册到Spring MVC
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**")  // 拦截所有请求
                .excludePathPatterns("/login", "/register");  // 排除不需要拦截的路径
    }
}

解释

  • @Configuration:注解表示这是一个Spring配置类,Spring在启动时会自动加载此类。
  • addInterceptors():重写此方法以注册拦截器。
  • InterceptorRegistry :这是一个注册拦截器的帮助类。通过 addInterceptor() 方法将自定义拦截器添加到注册表中。
  • addPathPatterns("/**"):拦截所有路径。
  • excludePathPatterns("/login", "/register"):排除登录和注册页面,这样这些页面不会被拦截。

拦截路径规则

  • addPathPatterns("/**") :表示拦截所有请求,/** 是一个通配符,表示所有的URL。
  • excludePathPatterns("/login", "/register") :排除某些路径,比如 /login/register 路径,这样用户访问这些路径时不会经过拦截器。

可以根据业务需求自定义要拦截的路径和排除的路径。

Spring Boot项目中的拦截器注册

Spring Boot 项目中,配置拦截器的步骤与上述步骤相同。Spring Boot在启动时会自动扫描带有 @Configuration 注解的类并加载配置。因此,在Spring Boot项目中,只需要定义拦截器并通过 WebMvcConfigurer 注册拦截器,Spring Boot就会自动应用这些配置。

测试拦截器

假设我们在Spring Boot应用程序中启动服务,并访问以下路径:

  • 访问 http://localhost:8080/home

    • 进入拦截器的 preHandle() 方法,输出 Request URL is http://localhost:8080/home,控制器方法执行完毕后进入 postHandle()afterCompletion() 方法。
  • 访问 http://localhost:8080/login

    • 因为 login 路径在 excludePathPatterns() 中排除,因此此请求不会进入拦截器。

多拦截器注册

在实际开发中,我们可能需要使用多个拦截器。可以在 addInterceptors() 方法中依次添加多个拦截器,Spring会按注册顺序依次调用 preHandle(),而 postHandle()afterCompletion() 方法按相反顺序执行。

示例:注册多个拦截器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册第一个拦截器
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/admin/**", "/user/**");

        // 注册第二个拦截器
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**");  // 拦截所有请求
    }
}

在这个例子中:

  • AuthInterceptor 负责处理权限认证,只拦截 /admin/**/user/** 的请求。
  • LogInterceptor 负责记录日志,拦截所有请求。

拦截器执行顺序

Spring MVC支持多个拦截器,如果你注册了多个拦截器,它们会按照注册的顺序执行。执行顺序如下:

  1. preHandle():按注册顺序执行。
  2. postHandle():按注册顺序的反向顺序执行。
  3. afterCompletion():按注册顺序的反向顺序执行。

四、常见拦截器的应用场景

  • 权限验证拦截器 :在 preHandle() 中检查用户是否具有访问某些资源的权限,若无权限则直接中断请求,返回错误信息。
  • 日志拦截器:记录请求的相关信息,比如请求的URL、请求的参数、响应时间等。
  • 性能监控拦截器 :在 preHandle() 中记录开始时间,在 afterCompletion() 中记录结束时间,并计算整个请求的处理时间。
  • 统一处理跨域问题的拦截器 :在 preHandle() 中设置响应头,允许跨域请求。
相关推荐
黄名富几秒前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想2 分钟前
JMeter 使用详解
java·jmeter
言、雲5 分钟前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇12 分钟前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
Yvemil740 分钟前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。42 分钟前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴1 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文1 小时前
宠物管理系统:Dao层
java·开发语言·宠物
王ASC2 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web
撒呼呼2 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot