HandlerInterceptor
类
在 Spring MVC 中,HandlerInterceptor
是一个强大的工具,它允许你在请求处理的不同阶段插入自定义逻辑。在这篇教程中,我们将深入探讨 HandlerInterceptor
的奥秘,帮助你从基础到高级应用,全面掌握这一技术。
基础概念
什么是 HandlerInterceptor
?
HandlerInterceptor
是 Spring MVC 中的一个接口,它提供了三个方法,用于在不同的阶段拦截请求和处理:preHandle
、postHandle
和 afterCompletion
。这些方法分别在请求处理的不同阶段被调用,让你能够在请求到达控制器之前、控制器处理请求之后以及整个请求完成后执行特定的操作。
查看代码
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
HandlerInterceptor
的作用
HandlerInterceptor
主要用于实现横切关注点,如日志记录、权限检查、事务管理等。通过使用拦截器,你可以将这类代码从业务逻辑中分离出来,从而提高代码的可维护性和可重用性。
方法详解
preHandle
方法
- 执行时机:在请求到达控制器之前被调用。
- 作用:可以用于进行权限检查、日志记录、请求验证等。
- 返回值:返回一个布尔值,表示是否继续执行后续的拦截器链和处理器(控制器方法)。如果返回
false
,则请求将被终止,不会调用后续的拦截器或处理器。
postHandle
方法
- 执行时机:在控制器处理请求之后,但在视图渲染之前被调用。
- 作用:可以用于添加或修改模型数据和视图。
- 执行顺序:在所有拦截器中,
postHandle
方法的执行顺序与preHandle
方法的执行顺序相反。
afterCompletion
方法
- 执行时机:在整个请求完成后被调用,即在视图渲染之后。
- 作用:可以用于资源清理、记录结果等。
- 执行顺序:无论请求处理过程中是否发生异常,
afterCompletion
方法都会被调用。在所有拦截器中,afterCompletion
方法的执行顺序也与preHandle
方法的执行顺序相反。
拦截器链
HandlerExecutionChain
类代表了一个处理器的执行链,这个链包括了处理器对象(通常是一个控制器方法)和一组拦截器。
类的定义
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
private int interceptorIndex = -1;
}
属性
handler
: 这是执行链中的处理器对象,通常是控制器中的一个方法。interceptorList
: 一个列表,包含所有注册的拦截器。interceptorIndex
: 当前拦截器的索引,用于在处理过程中跟踪。
构造函数
HandlerExecutionChain(Object handler)
: 使用指定的处理器对象创建一个新的执行链。HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors)
: 使用指定的处理器对象和拦截器数组创建一个新的执行链。HandlerExecutionChain(Object handler, List<HandlerInterceptor> interceptorList)
: 使用指定的处理器对象和拦截器列表创建一个新的执行链。
方法
getHandler()
: 返回执行链中的处理器对象。addInterceptor(HandlerInterceptor interceptor)
: 将指定的拦截器添加到执行链的末尾。addInterceptor(int index, HandlerInterceptor interceptor)
: 在指定的索引位置添加拦截器。addInterceptors(HandlerInterceptor... interceptors)
: 将一组拦截器添加到执行链的末尾。getInterceptors()
: 返回一个包含所有拦截器的数组。getInterceptorList()
: 返回一个不可修改的拦截器列表。
拦截器应用方法
applyPreHandle(HttpServletRequest request, HttpServletResponse response)
: 在处理器执行之前,按顺序调用所有注册拦截器的preHandle
方法。如果任何一个preHandle
方法返回false
,则不会执行处理器,并且调用triggerAfterCompletion
方法。applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
: 在处理器执行之后,逆序调用所有注册拦截器的postHandle
方法。triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
: 在请求完成或出现异常时,逆序调用所有已成功执行preHandle
方法的拦截器的afterCompletion
方法。applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response)
: 当并发处理开始时,逆序调用所有实现了AsyncHandlerInterceptor
接口的拦截器的afterConcurrentHandlingStarted
方法。
HandlerExecutionChain
是 Spring MVC 中实现请求拦截和处理流程控制的关键组件。通过使用拦截器,可以在请求处理的不同阶段插入自定义逻辑,例如权限检查、日志记录、事务管理等。这些拦截器与处理器一起构成了一个执行链,按照预定义的顺序执行。
工作原理
拦截器可以组成一个拦截器链。当一个请求进入时,它会按照拦截器链的顺序逐个执行每个拦截器的 preHandle
方法。如果 preHandle
方法返回 true
,则请求会继续传递给下一个拦截器,直到所有拦截器的 preHandle
方法都被调用。
当请求处理完成,即控制器的方法执行完毕后,会开始渲染视图。在视图渲染之后,拦截器链的 afterCompletion
方法会被依次调用,但是调用的顺序与 preHandle
方法的调用顺序相反。
设计目的
这种反向调用的设计是为了确保资源的正确释放和清理。例如,如果你在 preHandle
方法中分配了一些资源,你可以在对应的 afterCompletion
方法中释放这些资源。由于 afterCompletion
方法的调用顺序与 preHandle
相反,所以你可以确保资源的释放顺序与分配顺序相同,这样可以避免资源泄漏或其他依赖顺序的问题。
InterceptorRegistry
类
InterceptorRegistry
类用于配置和管理Spring MVC中的拦截器(HandlerInterceptor
)。
这个类位于org.springframework.web.servlet.config.annotation
包中,是Spring Web MVC配置相关的一部分。
类的主要功能
拦截器注册:
addInterceptor
: 添加一个HandlerInterceptor
类型的拦截器。返回一个InterceptorRegistration
对象,通过它可以进一步配置拦截器,比如添加它应该应用的URL模式或排除的URL模式。addWebRequestInterceptor
: 添加一个WebRequestInterceptor
类型的拦截器,并自动将其转换为HandlerInterceptor
适配器。
拦截器获取:
getInterceptors
: 返回所有注册的拦截器列表,按照它们的顺序进行排序。这里的顺序是指拦截器的优先级,通常通过实现Ordered
接口或使用@Order
注解来指定。
属性
private final List<InterceptorRegistration> registrations = new ArrayList<>();
InterceptorRegistration
是InterceptorRegistry
内部使用的类,用于封装拦截器的注册信息。它可以配置拦截器的属性,例如添加或排除特定的URL模式,这些配置最终会影响拦截器的应用范围。
类常量
INTERCEPTOR_ORDER_COMPARATOR
是一个比较器,用于确定拦截器的执行顺序。
private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR =
OrderComparator.INSTANCE.withSourceProvider(object -> {
if (object instanceof InterceptorRegistration interceptorRegistration) {
return (Ordered) interceptorRegistration::getOrder;
}
return null;
});
使用了Spring的OrderComparator
来比较对象。如果对象是InterceptorRegistration
类型,它会使用该注册的顺序值进行比较。
方法
addInterceptor
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
this.registrations.add(registration);
return registration;
}
- 这个方法允许添加一个
HandlerInterceptor
类型的拦截器。 - 它创建了一个
InterceptorRegistration
实例,并将其添加到registrations
列表中。 - 返回一个
InterceptorRegistration
对象,可以进一步配置拦截器,例如设置它应该应用到的URL模式。
addWebRequestInterceptor
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
InterceptorRegistration registration = new InterceptorRegistration(adapted);
this.registrations.add(registration);
return registration;
}
- 这个方法允许添加一个
WebRequestInterceptor
类型的拦截器。 - 它首先将
WebRequestInterceptor
适配为一个HandlerInterceptor
,通过WebRequestHandlerInterceptorAdapter
类。 - 然后创建一个
InterceptorRegistration
实例,并将其添加到registrations
列表中。 - 同样返回一个
InterceptorRegistration
对象以进行进一步配置。
getInterceptors
protected List<Object> getInterceptors() {
return this.registrations.stream()
.sorted(INTERCEPTOR_ORDER_COMPARATOR)
.map(InterceptorRegistration::getInterceptor)
.toList();
}
- 这个方法返回所有注册的拦截器对象,按顺序排列。
- 它使用Java Stream API对
registrations
列表进行处理,首先根据INTERCEPTOR_ORDER_COMPARATOR
进行排序,然后提取每个InterceptorRegistration
中的Interceptor
对象。 INTERCEPTOR_ORDER_COMPARATOR
是一个比较器,用于确定拦截器的执行顺序。如果拦截器实现了Ordered
接口,它会根据返回的顺序值进行排序。
使用场景
当我们在Spring MVC中想要添加自定义的拦截器时,通常是在配置类中实现WebMvcConfigurer
接口,并重写addInterceptors
方法。在这个方法中,我们可以使用InterceptorRegistry
来添加和配置你的拦截器。
InterceptorRegistration
类
InterceptorRegistration
是InterceptorRegistry
内部使用的类,用于封装拦截器的注册信息。它可以配置拦截器的属性,例如添加或排除特定的URL模式,这些配置最终会影响拦截器的应用范围。
属性
interceptor
:类型为HandlerInterceptor
,表示要注册的拦截器。includePatterns
:类型为List<String>
,表示拦截器应该包含的URL模式列表。excludePatterns
:类型为List<String>
,表示拦截器应该排除的URL模式列表。pathMatcher
:类型为PathMatcher
,用于匹配URL路径,默认是AntPathMatcher
。order
:类型为int
,表示拦截器的执行顺序。
主要职责
创建拦截器实例: 当一个拦截器被注册到InterceptorRegistry
时,InterceptorRegistration
对象会被创建并用于配置该拦截器的细节。这包括设置拦截器应适用的URL模式、排除的URL模式、自定义的路径匹配器以及拦截器的执行顺序。
配置拦截器: InterceptorRegistration
提供了几个方法来配置拦截器:
addPathPatterns
: 添加URL模式,拦截器将对这些模式下的请求进行拦截。excludePathPatterns
: 添加URL模式,拦截器将不会对这些模式下的请求进行拦截。pathMatcher
: 允许自定义路径匹配策略,这通常用于更复杂或特定的匹配规则。order
: 设置拦截器的执行顺序,较低的数值具有更高的优先级。
构建映射拦截器: 当配置完毕后,InterceptorRegistration
可以构建一个MappedInterceptor
实例(如果指定了URL模式)或直接返回HandlerInterceptor
实例(如果没有指定URL模式)。MappedInterceptor
是Spring MVC中用于处理带有URL模式的拦截器的类,它能够根据配置的URL模式决定是否应用拦截器。
实践案例
在Spring Boot中配置拦截器,我们需要实现HandlerInterceptor
接口或者继承HandlerInterceptorAdapter
类,并重写其中的方法,然后通过实现WebMvcConfigurer
接口的addInterceptors
方法来注册拦截器。
- 创建拦截器类:
查看代码
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 {
// 请求处理前调用
System.out.println("Pre-handle");
return true; // 如果返回true则继续流程,返回false则中断
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 请求处理后调用
System.out.println("Post-handle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求结束后调用
System.out.println("After-completion");
}
}
- 注册拦截器:
查看代码
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 {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**") // 添加拦截路径
.excludePathPatterns("/resources/**"); // 排除拦截路径
}
}
在上面的例子中,addPathPatterns
方法定义了拦截器应该拦截哪些路径,excludePathPatterns
方法定义了哪些路径不应该被拦截。例如,通常我们会排除静态资源路径,如CSS、JS、图片等,以避免不必要的拦截。
参考链接:
https://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html