一、Spring MVC拦截器的作用
拦截器是Spring MVC框架中处理 HTTP请求 的一种机制,通常用于在请求到达 控制器(Controller) 之前或从控制器返回结果之后进行额外的逻辑处理。可以用于以下场景:
- 日志记录:记录每次请求的开始时间、结束时间、请求的参数、响应的结果等。
- 认证和授权:在请求处理之前,检查用户的身份和权限。
- 统一异常处理:捕获请求处理中的异常,并返回统一的错误响应。
- 性能监控:记录请求处理的时间。
- 请求修改:在请求被控制器处理之前,修改请求数据。
拦截器的定义要分为两个部分:
- 拦截器核心实现:通过实现
HandlerInterceptor
接口定义拦截器的三大核心方法(preHandle
、postHandle
、afterCompletion
),实现请求拦截的核心逻辑。- 拦截器注册配置:通过实现
WebMvcConfigurer
接口的addInterceptors()
方法,将拦截器注册到应用中,并指定拦截规则(路径、排除路径等)
二、拦截器核心实现
通过实现 HandlerInterceptor 接口,定义具体的拦截逻辑,包括请求预处理、方法后处理以及请求完成后的清理操作。对应的三个方法是:
preHandle()
:在请求进入控制器方法之前执行。如果返回false
,则请求不会继续执行控制器方法;如果返回true
,则请求继续执行。postHandle()
:在控制器方法执行之后,但在视图渲染之前执行。可以对返回的数据进行修改。afterCompletion()
:在整个请求结束(即视图渲染完成)之后执行,通常用于资源清理等操作。
以上三个方法的重写都在实现HandlerInterceptor 接口的类中进行重写,如下面例子中的MyInterceptor类。
示例代码
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 请求预处理逻辑return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 请求后处理逻辑}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 请求完成后的清理操作}
}
1. preHandle()
方法
方法签名:
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()
示例:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取请求的URIString 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()
方法
方法签名:
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()
示例:
@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()
方法
方法签名:
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
参数就会包含这个异常信息;如果没有异常,ex
为null
。
常见参数使用:
request.getAttribute()
:从请求对象中获取之前设置的属性,比如在preHandle()
中设置的时间戳,用于计算请求处理时间。ex != null
:检查是否有异常,如果有异常则记录异常信息或进行相应处理。response.getStatus()
:获取响应的状态码,比如检查是否返回了500或其他错误状态码。
afterCompletion()
示例:
@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.三个方法的执行流程
preHandle()
:请求进入控制器之前执行。如果返回false
,请求将被终止,不会继续执行控制器方法;如果返回true
,则继续执行控制器方法。- 控制器方法执行:如果
preHandle()
返回true
,则会执行控制器方法。 postHandle()
:在控制器方法执行完之后,但在视图渲染之前执行。可以在此处修改返回的ModelAndView
或响应对象。- 视图渲染:将控制器方法返回的
ModelAndView
渲染为HTML或JSON响应。 afterCompletion()
:请求处理完成(包括视图渲染)之后执行。用于资源清理、日志记录或处理异常。
在 Spring Boot 或 Spring MVC 中,使用拦截器(Interceptor)是一种常见的方式来在 HTTP请求处理流程 的不同阶段执行额外的逻辑。要在Spring中注册拦截器,我们需要通过 Java配置,实现 WebMvcConfigurer
接口的 addInterceptors()
方法。
下面我将详细解释如何在 Spring Boot 或 Spring MVC 项目中通过实现 WebMvcConfigurer
接口来注册拦截器,并通过 addInterceptors()
方法来配置拦截器的路径规则。
三、拦截器注册配置
接下来,我们需要将拦截器注册到Spring MVC中。要做到这一点,我们需要创建一个Java配置类,并实现 WebMvcConfigurer
接口。然后在 addInterceptors()
方法中注册我们自定义的拦截器,定义拦截的 URL 路径或排除的路径。
我们需要创建一个带有 @Configuration
注解的配置类,它会在Spring启动时自动加载。
示例: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 {// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 将自定义拦截器注册到Spring MVCregistry.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()
方法按相反顺序执行。
示例:注册多个拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册第一个拦截器registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/admin/**", "/user/**");// 注册第二个拦截器registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**"); // 拦截所有请求}
}
在这个例子中:
AuthInterceptor
负责处理权限认证,只拦截/admin/**
和/user/**
的请求。LogInterceptor
负责记录日志,拦截所有请求。
拦截器执行顺序
Spring MVC支持多个拦截器,如果你注册了多个拦截器,它们会按照注册的顺序执行。执行顺序如下:
- preHandle():按注册顺序执行。
- postHandle():按注册顺序的反向顺序执行。
- afterCompletion():按注册顺序的反向顺序执行。
四、常见拦截器的应用场景
- 权限验证拦截器:在
preHandle()
中检查用户是否具有访问某些资源的权限,若无权限则直接中断请求,返回错误信息。 - 日志拦截器:记录请求的相关信息,比如请求的URL、请求的参数、响应时间等。
- 性能监控拦截器:在
preHandle()
中记录开始时间,在afterCompletion()
中记录结束时间,并计算整个请求的处理时间。 - 统一处理跨域问题的拦截器:在
preHandle()
中设置响应头,允许跨域请求。