文章目录
一、Spring Boot 拦截器是什么?
拦截器允许开发者在处理 HTTP 请求的生命周期的不同阶段—即请求处理之前、之后以及请求完成后—执行特定的操作。这些操作可以包括日志记录、身份验证、权限检查、事务管理等。
1. 拦截器与过滤器的区别
理解拦截器之前,有必要区分两个经常被混用的概念:拦截器(Interceptor)和过滤器(Filter)。
过滤器(Filter)
过滤器是 Java Servlet 规范的一部分,它在 Servlet 层面上提供了一种方式来处理请求和响应。过滤器可以对几乎所有的请求进行拦截,因为它们工作在请求的前端(在请求达到控制器之前)和后端(在响应被发送回客户端之前)。过滤器主要用于请求的预处理、响应的后处理,以及请求链中的请求/响应转换。
拦截器(Interceptor)
与过滤器相比,拦截器是 Spring MVC 提供的,它提供了更精细的控制。拦截器仅作用于处理器映射到的方法。这意味着,它们可以访问控制器执行的上下文,包括执行的控制器本身和控制器方法的元数据。拦截器可以在以下三个阶段执行操作:
- preHandle:在 Controller 方法调用之前执行。可以进行身份验证、权限检查等操作,并决定是否中断执行链,即是否将请求传递给控制器处理。
- postHandle:在 Controller 方法调用之后,但在视图被渲染之前执行。可以对模型数据进行操作或对视图进行处理。
- afterCompletion:在整个请求结束后执行,即在视图渲染完毕后。这里可以进行资源清理等操作。
拦截器提供了一种更为灵活和强大的方式来插入跨越请求处理生命周期的逻辑。相比之下,过滤器更适合处理如请求日志记录、请求和响应的通用处理(比如设置响应头)等跨应用程序的共通行为。
二、Spring Boot 拦截器的使用场景
身份验证和授权
身份验证和授权是几乎每个应用必须处理的关键安全问题。通过拦截器,在请求到达目标控制器之前,可以检查用户的认证状态和权限。如果用户未经认证或缺乏访问特定资源的权限,拦截器可以阻止请求继续处理并直接返回一个错误响应或重定向到登录页面。这样的处理确保了应用的安全性,同时保持了控制器逻辑的清晰和专注。
日志记录
日志记录是开发和维护过程中的一个重要方面,它帮助开发者了解应用的运行状态,以及在出现问题时进行调试。通过拦截器,可以在请求处理的前后记录下请求路径、请求参数、用户身份等详细信息。这不仅有助于监控应用的健康状况,也为故障排查和性能分析提供了宝贵信息。
性能监测
在性能敏感的应用中,监测和优化请求的处理时间是提高用户体验的关键。拦截器可以用于监控请求的处理时间,通过记录请求开始和完成的时间,可以计算出处理每个请求所需的时间。这些数据可以用于识别性能瓶颈,指导后续的优化工作。
通用行为的应用
在多个控制器或请求处理流程中需要应用的通用行为,如跨域资源共享(CORS)设置、语言偏好处理、时区信息设置等,都可以通过拦截器以统一和非侵入式的方式实现。例如可以在拦截器中统一处理跨域请求的头信息,或根据请求头或参数调整语言和时区设置,从而提高代码的复用性和维护性。
三、如何实现 Spring Boot 拦截器
1. 创建拦截器类
首先需要定义一个类实现 HandlerInterceptor
接口或继承 HandlerInterceptorAdapter
类。这个接口定义了三个方法:preHandle
、postHandle
和 afterCompletion
,分别对应于请求处理的前、中、后三个阶段。
- preHandle:在请求处理之前进行调用(Controller方法调用之前)。这里可以进行权限验证、日志记录等操作。如果返回值为
false
,Spring MVC将不会继续处理这个请求。 - postHandle:在请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)。可以通过这个方法对模型数据进行处理或对视图进行处理。
- afterCompletion:在整个请求结束之后被调用,也就是在
DispatcherServlet
渲染了对应的视图之后执行(主要用于进行资源清理工作)。
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在Controller执行之前调用
// 这里可以进行权限验证、日志记录等
return true; // 返回true则继续执行下一个拦截器或处理器
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在Controller执行之后调用,但在视图渲染之前
// 这里可以对模型数据进行处理或对视图进行处理
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在请求完全结束后调用,可用于清理资源等
}
}
2. 注册拦截器
创建拦截器类之后,需要将其注册到 Spring MVC 的拦截器链中以启用其功能。可以通过实现 WebMvcConfigurer
接口并重写 addInterceptors
方法来完成。
在 WebMvcConfigurer
中可以定义拦截器的应用规则,比如指定拦截器应该拦截哪些URL路径。通过调用 InterceptorRegistry
的 addInterceptor
方法,并通过 addPathPatterns
方法来指定路径模式,可以非常灵活地配置拦截器的应用范围。
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 WebConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器,并指定拦截的路径
registry.addInterceptor(myInterceptor).addPathPatterns("/**");
// 你也可以通过addPathPatterns添加多个匹配模式,或通过excludePathPatterns排除某些模式
}
}
四、高级配置
1. 拦截器链
在 Spring Boot 应用中,可以通过配置多个拦截器来形成一个拦截器链。Spring MVC 会按照拦截器注册的顺序依次调用这些拦截器,执行它们的 preHandle
、postHandle
和 afterCompletion
方法。
配置拦截器链
在 WebMvcConfigurer
的实现类中,可以通过调用 addInterceptors
方法多次添加多个拦截器,从而形成一个链。每个拦截器可以定义自己的拦截路径模式,也可以有共同的路径模式。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
registry.addInterceptor(new SecondInterceptor()).addPathPatterns("/api/**");
}
这个例子中,所有请求都会被 FirstInterceptor
拦截,而只有 /api/**
路径的请求会被 SecondInterceptor
拦截。
2. 动态拦截器
动态拦截器允许开发者根据请求的动态信息,如 URL、Header 等,来决定是否执行拦截器。可以通过在 preHandle
方法中添加逻辑来实现。
实现动态拦截
public class DynamicInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 根据请求的 URL 或 Header 决定是否进行拦截
String headerValue = request.getHeader("X-Intercept");
if ("true".equals(headerValue)) {
// 执行某些操作
return true; // 继续执行拦截器链
} else {
// 跳过当前拦截器,不对请求进行处理
return false; // 中断执行,这将阻止控制器方法的执行
}
}
}
这个例子中,拦截器会检查请求头 X-Intercept
的值,根据这个值来决定是否进行后续操作。
3. 异常处理
在拦截器中处理异常
拦截器本身并不直接处理异常。相反,应当将异常信息传递给 Spring MVC 的异常处理机制。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
// 记录异常信息,或者使用其他方式处理异常
// 例如,可以将异常信息包装后抛出自定义异常
throw new CustomException("Interceptor error", ex);
}
}
在这个例子中,如果 afterCompletion
方法接收到异常,它会记录异常信息或抛出自定义异常。应用中应该有全局异常处理器(如使用 @ControllerAdvice
注解的类)来进一步处理这些异常,例如返回一个友好的错误响应给用户。