IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringMVC HandlerInterceptor拦截器解析及源码 -> 正文阅读

[Java知识库]SpringMVC HandlerInterceptor拦截器解析及源码

SpringMVC HandlerInterceptor拦截器源码解析


提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、实例

写一个类继承HandlerInterceptor接口,重写其中的方法

1.单个拦截器

public class MyFirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle.....");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle......");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion......");
    }
}

注册

    <!-- 拦截器  -->
    <mvc:interceptors>
        <bean class="edu.qust.controller.MyFirstInterceptor"></bean>
    </mvc:interceptors>

Controller

@Controller
public class InterceptorTestController {
    @RequestMapping("/test01")
    public String test01(){
        System.out.println("test01方法调用");
        return "success";
    }
}

执行顺序
在这里插入图片描述

单个拦截器的执行顺序

return true并且无异常

preHandle ? 调用方法 ? postHandle ? 到达页面 ? afterCompletion

return false 后续内容将会被拦截,不再执行
 @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle.....");
        return false;
    }

在这里插入图片描述

return true 有异常 仍会执行afterCompletion,但不执行postHandle()

在这里插入图片描述

在这里插入图片描述

2.多个拦截器

执行顺序取决于配置顺序
配置顺序,这里是先配置的2号拦截器,即preHandle…222

 <!-- 拦截器  -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/test01"/>
            <bean class="edu.qust.controller.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

    <mvc:interceptors>
        <bean class="edu.qust.controller.MyFirstInterceptor"></bean>
    </mvc:interceptors>

新增的secondInterceptor

public class MySecondInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle.....222");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle......222");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion......222");
    }

}

两个preHandle都return true的情况
虽然名字是2号,但因为配置顺序是先2后1,所以会先执行2
在这里插入图片描述

多个拦截器执行顺序

(先配置的)preHandle ? (后配置的)preHandle ? 调用方法 ? (后配置的)postHandle ? (先配置的)postHandle ? 到达页面 ? (后配置的)afterCompletion ? (先配置的)afterCompletion

preHandle()==>谁配置在前谁先,post/after ? 的谁配置在后谁先

上面是两个拦截器的handle都return true的情况,现在设置return false的情况

让2号return true ,1号 return false的情况
在这里插入图片描述
发现2号的afterCompletion执行了 ==>放行preHandle的会执行自己的afterCompletion
所以没有放行的1号并没有执行自己的afterCompletion

那么为什么会这样执行,放行了preHandle的拦截器为什么会执行自身的afterCompletion

二、源码 DispatcherServlet.class中寻找原因

1.找到preHandle()执行的位置

找到在DispatcherServlet.class实际干活的doDispatch(),开始debug
在这里插入图片描述
在执行getHandler中发现,拿到的不只是可以用来处理的类handler,还有拦截器集合interceptorList
在这里插入图片描述
发现其中有3个拦截器
还有一个默认为-1的拦截器索引
在这里插入图片描述
1和2是我们自己设计的拦截器

接着向下执行

执行到了下面这句时,控制台打印了
在这里插入图片描述在这里插入图片描述
看英文能看出来 applyPreHandle 就是 执行preHandle嘛

此时我们找到了拦截器执行preHandle()的位置

   if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}

这下得进去看看为什么放行了preHandle就会执行他的afterCompletion

 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }
        return true;
    }

这时候之前拿到的拦截器集合就出现了,可以发现是做遍历拦截器的操作,如果当前拦截器的preHandle返回的是false则会执行if语句里的内容 ===> 调用triggerAfterCompletion,然后return false直接结束

这里要注意 :源码中的 triggerAfterCompletion ≠ afterCompletion

这里很容易误会成返回false才执行afterCompletion,这不就成了返回false才执行afterCompletion了吗? 然而他们俩并不是一个方法,上面的例子很明显说明了

         			      preHandle返回了true才执行自身的afterCompletion

看for循环中,出现了之前的拦截器索引 interceptorIndex ,之前这个数值为 -1 ,现在做自增循环,很明显,这是在记录个数,以本身为下标,一直+1 ,直到出现了第一个return false的拦截器出现。

遍历的顺序就是配置的顺序
在这里插入图片描述
在执行到第3个(下标为2)的MyFirstInterceptor时,因为设置了return false,所以执行了if中的语句
在这里插入图片描述
进入this.triggerAfterCompletion(request, response, (Exception)null);

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.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 var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }

发现还是在遍历之前的拦截器集合,但这次看到了真正的afterCompletion

看这次的for循环,i是基于之前interceptorIndex做自减循环,而interceptorIndex则是用来记录有多少return true的拦截器,本身就是数组的下标。

这下就清楚了,这是把interceptorIndex作为下标,把集合中interceptorIndex之前的拦截器的afterCompletion都执行一遍。所以就实现了return true的会执行afterCompletion的效果

所以return true的2号会打印afterCompletion,而return false的1号则没有执行

现在把两个都改成return true再debug,不然下面会被拦截不执行

找完问题的原因后继续向下走,接着执行

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

在这里插入图片描述
(mv的源码分析补在以后)

2.找到preHandle()执行的位置

继续执行
在这里插入图片描述
在这里插入图片描述
找到了执行postHandle的位置

mappedHandler.applyPostHandle(processedRequest, response, mv);

进去看看如何执行postHandle()

  void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.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);
            }
        }

    }

发现依旧是遍历,但是这次是倒叙遍历,这也就说明了为什么先配置的拦截器会后执行postHandle()

回到doDispatch(),继续走,进入有渲染页面方法的processDispatchResult()中

3.afterCompletion相关

 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }

执行到在这里插入图片描述
进行页面渲染,控制台打印
在这里插入图片描述
若this.render正常执行,接着则执行triggerAfterCompletion去执行afterCompletion

  if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}

若this.render有问题,下面的catch也会捕获,接着执行triggerAfterCompletion,这就是为什么i=1/0这种异常程序依旧会执行afterCompletion的原因

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:36:58  更:2021-07-30 12:38:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/4 2:17:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码