Spring MVC拦截器HandlerInterceptor全解!
Spring MVC拦截器(HandlerInterceptor)是一个十分重要且常用的功能,是我们学习和使用Spring MVC必须掌握的基础技能之一。
HandlerInterceptor和Servlet规范中的Filter类似,都可以用来对请求进行拦截。不同的是,Filter针对的是servlet,而HandlerInterceptor针对的是handler。
1 拦截器工作原理
org.springframework.web.servlet.HandlerInterceptor源码:
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的三个方法会在不同的请求处理节点进行调用:
preHandle():在handler执行前调用。postHandle():在handler执行后调用。afterCompletion():在请求处理完后调用。
在项目初始化时,会将配置的一系列HandlerInterceptor按顺序收集起来,设置为HandlerMapping的成员变量adaptedInterceptors。
在处理请求时,通过HandlerMapping的getHanlder()方法,一方面会获取请求映射的handler,另一方面会从adaptedInterceptors获取拦截器,共同构造HandlerExecutionChain返回。
在后续流程中,会依次在各个节点调用HandlerInterceptor的方法。
下图表示完整请求流程中HandlerInterceptor调用的节点:

如果在preHandle()阶段就有某个拦截器校验不通过,会从上一个拦截器开始执行afterCompletion()进行返回,流程如下:

HandlerInterceptor的执行顺序是通过HandlerExecutionChain的interceptorIndex成员变量决定的,它的默认值是-1。
上述流程图的执行原理可以通过相关源码验证,HandlerExecutionChain#applyPreHandle源码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
HandlerExecutionChain#applyPostHandle源码如下:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
HandlerExecutionChain#triggerAfterCompletion源码如下:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
2 拦截器配置流程
Spring MVC拦截器的使用十分简单,只需要两个步骤:
- 实现
HandlerInterceptor接口,自定应拦截规则。 - 将自定义拦截器添加到
HandlerMapping成员变量中。
Spring提供了通过在WebMvcConfigurer中注册拦截器的方式:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
在项目启动时,DelegatingWebMvcConfiguration会加载所有WebMvcConfigurer类型的bean,缓存到configurers成员变量中:
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}
DelegationWebMvcConfiguration的父类WebMvcConfigurationSupport会初始化requestMappingHandlerMapping、beanNameHandlerMapping以及routerFunctionMapping等基础HandlerMapping实现类。
在初始化过程中,会从上述configurers中获取配置的拦截器,设置到各个HandlerMapping实现类中。
例如,requestMappingHandlerMapping初始化设置拦截器流程如下:
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
// 省略……
return mapping;
}
WebMvcConfigurationSupport#getInterceptors方法源码如下:
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
其中,DelegatingWebMvcConfiguration#addInterceptors方法会从configurers中添加开发人员自定义的拦截器:
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
WebMvcConfigurerComposite#addInterceptors会遍历所有WebMvcConfigurer实现类,调用其addInterceptors()方法(也就是开发人员注册拦截器的方法),加载所有自定义的拦截器:
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
自此,完成了自定义拦截器的加载流程,我们可以总结出流程图如下:

其他小细节:
- 在
HandlerMapping初始化过程中,会添加一些默认的拦截器,例如ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor,感兴趣的可以自行去查看相关源码。 - 跨域拦截器是在请求处理过程中,根据跨域配置动态添加的,无需自定拦截器。

浙公网安备 33010602011771号