SpringMVC拦截器HandlerInterceptor原理及使用
在使用SpringMVC拦截器的时候,我们接触的最多的便是HandlerInterceptor接口,因为我们所有的自定义拦截器都必须要实现HandlerInterceptor接口,那么就先从HandlerInterceptor接口开始一步步分析。
一、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 {}
根据注释部分我们知道如下内容:
preHandle是在找到处理handler对象的HandlerMapping之后,HandlerAdapter调度handler之前执行。
postHandle是在HandlerAdapter调度handler之后,DispatcherServlet渲染视图之前执行,可以通过ModelAndView来向视图中添加一些信息等。
afterCompletion是在渲染视图结束后执行,主要可以用来进行事后的资源清理。
其中
postHandle和afterCompletion方法是反顺序执行的。也就是说第一个拦截器会最后一个执行。
关于HandlerInterceptor的执行顺序我们可以在HandlerExecutionChain类中找到。
二、HandlerExecutionChain类
这个类由一个handler和若干的HandlerInterceptor构成。那么这个类的作用就显而易见了,就是将拦截器和handle组合起来执行。就是对handle进行了包装。
这个类中有几个主要的方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//按顺序执行for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {//逆序执行for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {// 逆序执行 for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}
}
从函数名中我们可以看出这些方法分别是做什么的,分别是执行interceptorList中所有interceptor的preHandle、postHandle和afterCompletion方法
。
先从applyPreHandle()看起,我们发现这个方法就是做的这样一个工作,
按照列表中interceptor的顺序来执行它们的preHandle方法,直到有一个返回false。
再看一下返回false后这个方法所做的工作,这时会调用triggerAfterCompletion方法,此时this.interceptorIndex指向上一个返回true的interceptor的位置,所以它会按逆序执行所有返回true的interceptor的afterCompletion方法
。换言之,也就是对于任意的返回false的interceptor都不会执行afterCompletion方法。而且是中断之前所有的preHandle执行完成之后才会执行afterCompletion方法。接下来是applyPostHandle(),这个方法较为简单,就是按照逆序执行所有interceptor的postHandle方法。最后的triggerAfterCompletion()也是一样,就是从最后一次preHandle成功的interceptor处逆序执行afterCompletion。
三、HandlerMapping接口
HandlerExecutionChain是通过HandlerMapping的getHandler方法返回的
。继承该接口的类是来实现请求和handler对象的映射关系
的。
这个接口中只有这样一个方法
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
根据函数名,参数及返回值我们不难猜出这个接口的作用,就是根据request返回HandlerExecutionChain。
至于HandlerMapping在springMVC中有多种实现,我们此处就不深究了。
对于getHandler最后的调度部分便是springMVC的最外层DispatcherServlet类了
四、DispatcherServlet类
DispatcherServlet类中调用HandlerMapping的getHandler的方法为getHandler(同名)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}}return null;
}
从代码中不难看出整个逻辑就是依次判断servlet中的每个handlerMapping是否能够匹配该请求,直到找到那个匹配的然后返回处理结果。
对于HandlerExecutionChain的调用我们可以在doDispatch()中找到
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 此处用processedRequest 需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;//主要用来管理异步请求的处理。什么时候要用到异步处理呢?就是业务逻辑复杂(或者其他原因),为了避免请求线程阻塞,需要委托给另一个线程的时候。WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//检查是否为上传文件请求//checkMultipart 这个方法很重要,判断是否是上传需求。且看下面的具体分析://如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求processedRequest = checkMultipart(request);//标记一下:是否是文件上传的requestmultipartRequestParsed = (processedRequest != request);// 步骤1,获取执行链,重要// 找到一个处理器,如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 步骤2,获取适配器,重要// 根据实际的handler去找到一个合适的HandlerAdapter,方法详细逻辑同getHandler,因此不再解释HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//如果是GET请求,如果内容没有变化的话,则直接返回String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 步骤3,执行拦截器pre方法,重要// 这段代码很有意思:执行处理器连里的拦截器们,具体参阅下面详细:if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//步骤4,真正处理逻辑,重要// 真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView// 这也是一个很复杂的过程(序列化、数据绑定等等),需要后面专题讲解mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的if (asyncManager.isConcurrentHandlingStarted()) {return;}//意思是:如果我们没有设置viewName,就采用默认的。否则采用我们自己的applyDefaultViewName(processedRequest, mv);//步骤5,执行拦截器post方法,重要// 执行所有的拦截器的postHandle方法,并且把mv给他// 这里有一个小细节:这个时候拦截器是【倒序】执行的mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}//步骤6,处理视图,重要//这个方法很重要,顾名思义,他是来处理结果的,渲染视图、处理异常等等的 下面详细分解processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {//步骤7,执行拦截器收尾方法,重要triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
从上面代码中我们可以验证第一部分所说的HandlerInterceptor接口中三个方法的执行顺序:
- preHandle是
在找到处理handler对象的HandlerMapping之后,HandlerAdapter调度handler之前执行。
- postHandle是
在HandlerAdapter调度handler之后,DispatcherServlet渲染视图之前执行
。- afterCompletion是
在渲染视图结束后执行
。
五、例子
- 四个拦截器A\B\C\D
@Component
public class A implements HandlerInterceptor {//在Controller执行之前调用,如果返回false,controller不执行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("---------A.preHandle--------");return true;}//controller执行之后,且页面渲染之前调用@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("---------A.postHandle--------");}//页面渲染之后调用,一般用于资源清理操作@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("---------A.afterCompletion--------");}
}
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new A());registry.addInterceptor(new B());registry.addInterceptor(new C());registry.addInterceptor(new D());WebMvcConfigurer.super.addInterceptors(registry);}
}
当全部拦截器的preHandle返回值都是true时,调用拦截器返回如下:
当C的preHandle返回false时,返回如下:
SpringMVC拦截器HandlerInterceptor原理及使用相关推荐
- SpringMVC拦截器HandlerInterceptor拦截后返回数据或视图View
SpringBoot版本:2.1.6.RELEASE SpringMVC版本:5.1.8.RELEASE SpringMVC拦截器 比如说在SpringMVC Web环境下,需要实现一个权限拦截的功能 ...
- SpringMVC拦截器HandlerInterceptor使用
Spring MVC 拦截器(HandlerInterceptor)使用 Spring 拦截器--HandlerInterceptor 转载于:https://www.cnblogs.com/goto ...
- java面试 拦截器问题_面试必问:给我说一下Spring MVC拦截器的原理?
拦截器是每个Web框架必备的功能,也是个老生常谈的主题了.本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍1. HandlerExecutionCh ...
- 面试:给我说一下Spring MVC拦截器的原理?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://urlify.cn/namaQ ...
- 面试官:给我说一下 Spring MVC 拦截器的原理?
前言 SpringMVC是目前主流的Web MVC框架之一. 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原 ...
- SpringMVC拦截器与Filter过滤器
SpringMVC拦截器与Filter过滤器 SpringMVC拦截器与Filter过滤器 Spring MVC拦截器的定义 SpringMVC拦截器的配置 SpringMVC拦截器HandlerIn ...
- SpringMVC→拦截器、SpringMVC拦截器实现、多个拦截器工作原理、拦截器使用场景、拦截器Interceptor与过滤器Filter区别
拦截器 拦截器实现 多个拦截器工作原理 拦截器使用场景 请求编码设置及请求登录Session校验 使用时间段设置 拦截器Interceptor与过滤器Filter区别
- springMVC 拦截器
为什么80%的码农都做不了架构师?>>> 实现springMVC 拦截器步骤: 1.定义拦截器类××××HandlerInterceptor 继承HandlerInterce ...
- springmvc 拦截器、国际化、验证
2019独角兽企业重金招聘Python工程师标准>>> springmvc 拦截器 继承了HandlerIntercepter的类可以作为拦截器类: package com.yawn ...
最新文章
- android window 大小,android popupWindow 中宽度莫名很大,求帮助?
- php tp3 操作绑定到类,快速入门 17:操作绑定到类
- Git复习(十一)之常见命令用法
- 微信朋友圈五月十大谣言:60岁以上老人打962899可享免费服务
- MAC下MySQL初始密码忘记怎么办
- AJAX 跨域访问 — 方法大全
- CBR编码与VBR编码
- USYD悉尼大学DATA 2002 【R语言学习1】【介绍R】Introduction to R「虽迟但到」
- MapReduce剥洋葱
- 关于25Qxx踩坑总结(无法写入)
- Android点选下拉列表框选项,获取选项内容
- Mybatis配置分页
- 移动端弹出层滚动时禁止body滚动
- 迪米特法则(LOD)
- MySQL命令rename:修改表名
- TCPDF生成PDF文件方法教程说明
- js处理本地.bin音频文件和node.js的fs模块处理本地.bin音频区别
- 51单片机在Ubuntu和MacOS下程序开发和下载
- TB6600驱动器调试42电机(stm32)
- 产品经理(14)-金融圈需求app