Springboot拦截器使用及其底层源码剖析

博主最近看了一下公司刚刚开发的微服务,准备入手从基本的过滤器以及拦截器开始剖析,以及在帮同学们分析一下上次的jetty过滤器源码与本次Springboot中tomcat中过滤器的区别。正题开始,拦截器顾名思义是进行拦截请求的一系列操作。先给大家示例一下使用操作

1 @Configuration
2 public class WebConfiguration implements WebMvcConfigurer {
3 
4     @Override
5     public void addInterceptors(InterceptorRegistry registry) {
6         registry.addInterceptor(new TstCfg());
7     }
8 }

 1 /**2  * @title: TstCfg3  * @Author junyu4  * 旧巷里有一个穿着白衬衫笑起来如太阳般温暖我的少年。5  * 记忆里有一个穿着连衣裙哭起来如孩子般讨人喜的女孩。6  * 他说,哪年树弯了腰,人见了老,桃花落了白发梢,他讲的笑话她还会笑,那便是好。7  * 她说,哪年国改了号,坟长了草,地府过了奈何桥,她回头看时他还在瞧,就不算糟。8  * @Date: 2020/7/29 11:539  * @Version 1.0
10  */
11 public class TstCfg extends HandlerInterceptorAdapter {
12 
13     @Override
14     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
15         System.out.println("前");
16         return super.preHandle(request, response, handler);
17     }
18 
19     @Override
20     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
21         System.out.println("后");
22     }
23 
24     @Override
25     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
26         System.out.println("一直会出现");
27         System.out.println(1/0);
28     }
29 }

  首先我们可能会想到,我们的拦截器是何时装配到拦截器数组中

  其实就是在springboot启动时执行doCreateBean时,进行调用创建的org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration会在这里放入进去所有实现了WebMvcConfigurer接口的类,一共有7个,其中就有我们自己实现了WebMvcConfigurer接口的WebConfiguration类,

  我们的写的配置类WebConfiguration,继承了WebMvcConfigurer并重写了addInterceptors方法,所以我们的拦截器就在这时候装配进去了。这次知道为什么我们写的配置拦截器的配置示例需要继承------WebMvcConfigurer,我们当然也可以去继承已经实现了这个类的其他类,因为都可以去添加拦截器,博主亲试过,所以就不贴图了!

 

   好了,拦截器已经添加完了,那什么时候调用我们拦截器呢?一步一步脚印来,当浏览器请求我们地址的 时候,分一下几步:

 第一步:tomcat容器首先会接受到请求,这里将会走DispatcherServlet,看到这个大家都熟悉了。

 

 第二步:当然不会先走我们的拦截器了,我们的拦截器是在Springboot框架进行管理的,现在还在servlet,所以会先走到filter过滤器这一步,来贴图:官方代码太长,一屏截不下,前面有一个创建过滤器链的过程:等下次在给大家讲一下jetty的过滤器链与tomcat的过滤器链的区别

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

 第三步:所以一旦连过滤器都没通过的话,会直接return回去,不会再进行拦截器的调用。来贴代码,过滤器通过后如何调用我们拦截器的

 1 private void internalDoFilter(ServletRequest request,2                                   ServletResponse response)3         throws IOException, ServletException {4         //这里将会调用所有过滤器链的过滤器,不做重点讲解了,看看下面拦截器的调用5         // Call the next filter if there is one6         if (pos < n) {7             ApplicationFilterConfig filterConfig = filters[pos++];8             try {9                 Filter filter = filterConfig.getFilter();
10 
11                 if (request.isAsyncSupported() && "false".equalsIgnoreCase(
12                         filterConfig.getFilterDef().getAsyncSupported())) {
13                     request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
14                 }
15                 if( Globals.IS_SECURITY_ENABLED ) {
16                     final ServletRequest req = request;
17                     final ServletResponse res = response;
18                     Principal principal =
19                         ((HttpServletRequest) req).getUserPrincipal();
20 
21                     Object[] args = new Object[]{req, res, this};
22                     SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
23                 } else {
24                     filter.doFilter(request, response, this);
25                 }
26             } catch (IOException | ServletException | RuntimeException e) {
27                 throw e;
28             } catch (Throwable e) {
29                 e = ExceptionUtils.unwrapInvocationTargetException(e);
30                 ExceptionUtils.handleThrowable(e);
31                 throw new ServletException(sm.getString("filterChain.filter"), e);
32             }
33             return;
34         }
35 
36         // We fell off the end of the chain -- call the servlet instance
37         try {
38             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
39                 lastServicedRequest.set(request);
40                 lastServicedResponse.set(response);
41             }
42 
43             if (request.isAsyncSupported() && !servletSupportsAsync) {
44                 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
45                         Boolean.FALSE);
46             }
47             // Use potentially wrapped request from this point
48             if ((request instanceof HttpServletRequest) &&
49                     (response instanceof HttpServletResponse) &&
50                     Globals.IS_SECURITY_ENABLED ) {
51                 final ServletRequest req = request;
52                 final ServletResponse res = response;
53                 Principal principal =
54                     ((HttpServletRequest) req).getUserPrincipal();
55                 Object[] args = new Object[]{req, res};
56                 SecurityUtil.doAsPrivilege("service",
57                                            servlet,
58                                            classTypeUsedInService,
59                                            args,
60                                            principal);
61             } else {
62                 //过滤器终于完事了,现在终于开始正式调用我们的方法了,我们看看service方法做了什么吧!
63                 servlet.service(request, response);
64             }
65         } catch (IOException | ServletException | RuntimeException e) {
66             throw e;
67         } catch (Throwable e) {
68             e = ExceptionUtils.unwrapInvocationTargetException(e);
69             ExceptionUtils.handleThrowable(e);
70             throw new ServletException(sm.getString("filterChain.servlet"), e);
71         } finally {
72             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
73                 lastServicedRequest.set(null);
74                 lastServicedResponse.set(null);
75             }
76         }
77     }    

其实最终它会调用到DispatcherServlet的doDispatch方法

 1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {2         HttpServletRequest processedRequest = request;3         HandlerExecutionChain mappedHandler = null;4         boolean multipartRequestParsed = false;5 6         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);7 8         try {9             ModelAndView mv = null;
10             Exception dispatchException = null;
11 
12             try {
13                 processedRequest = checkMultipart(request);
14                 multipartRequestParsed = (processedRequest != request);
15 
16                 // Determine handler for the current request.
17                 mappedHandler = getHandler(processedRequest);
18                 if (mappedHandler == null) {
19                     noHandlerFound(processedRequest, response);
20                     return;
21                 }
22 
23                 // Determine handler adapter for the current request.
24                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
25 
26                 // Process last-modified header, if supported by the handler.
27                 String method = request.getMethod();
28                 boolean isGet = "GET".equals(method);
29                 if (isGet || "HEAD".equals(method)) {
30                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
31                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
32                         return;
33                     }
34                 }
35                 //所有拦截器开始在调用方法前拦截,如果你拦截器中返回false,则直接return不会再调用该方法!下面有源代码
36                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
37                     return;
38                 }
39 
40                 // Actually invoke the handler.
41                 //底层进行invoke反射,调用当前请求的方法,不用再往里面看了
42                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
43 
44                 if (asyncManager.isConcurrentHandlingStarted()) {
45                     return;
46                 }
47 
48                 applyDefaultViewName(processedRequest, mv);
49                 //调用拦截器的postHandle,下面有源代码
50                 mappedHandler.applyPostHandle(processedRequest, response, mv);
51             }
52             catch (Exception ex) {
53                 dispatchException = ex;
54             }
55             catch (Throwable err) {
56                 // As of 4.3, we're processing Errors thrown from handler methods as well,
57                 // making them available for @ExceptionHandler methods and other scenarios.
58                 dispatchException = new NestedServletException("Handler dispatch failed", err);
59             }
60             //该方法中多做了一些逻辑,其实最后也调用了triggerAfterCompletion方法,最终调用拦截器方法的afterCompletion方法
61             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
62         }
63         catch (Exception ex) {
64             //所以不管是否出现异常,拦截器方法的afterCompletion方法是一定会调用的!
65             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
66         }
67         catch (Throwable err) {
68             //所以不管是否出现异常,拦截器方法的afterCompletion方法是一定会调用的!
69             triggerAfterCompletion(processedRequest, response, mappedHandler,
70                     new NestedServletException("Handler processing failed", err));
71         }
72         finally {
73             if (asyncManager.isConcurrentHandlingStarted()) {
74                 // Instead of postHandle and afterCompletion
75                 if (mappedHandler != null) {
76                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
77                 }
78             }
79             else {
80                 // Clean up any resources used by a multipart request.
81                 if (multipartRequestParsed) {
82                     cleanupMultipart(processedRequest);
83                 }
84             }
85         }
86     }

现在终于开始了我们拦截器的方法了,一个一个来:

 1 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {2         HandlerInterceptor[] interceptors = getInterceptors();3         if (!ObjectUtils.isEmpty(interceptors)) {4             for (int i = 0; i < interceptors.length; i++) {5                 HandlerInterceptor interceptor = interceptors[i];6                 //调用所有拦截器的preHandle方法7                 if (!interceptor.preHandle(request, response, this.handler)) {8                     //就算preHandle方法没有通过,仍然会调用这个triggerAfterCompletion方法。9                     triggerAfterCompletion(request, response, null);
10                     return false;
11                 }
12                 this.interceptorIndex = i;
13             }
14         }
15         return true;
16     }

 1 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)2             throws Exception {3 4         HandlerInterceptor[] interceptors = getInterceptors();5         if (!ObjectUtils.isEmpty(interceptors)) {6             for (int i = interceptors.length - 1; i >= 0; i--) {7                 HandlerInterceptor interceptor = interceptors[i];8                 //调用拦截器的postHandle方法,9                 interceptor.postHandle(request, response, this.handler, mv);
10             }
11         }
12     }

 1 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)2             throws Exception {3 4         HandlerInterceptor[] interceptors = getInterceptors();5         if (!ObjectUtils.isEmpty(interceptors)) {6             for (int i = this.interceptorIndex; i >= 0; i--) {7                 HandlerInterceptor interceptor = interceptors[i];8                 try {9                     //调用拦截器的afterCompletion方法,不管是否异常都会进行调用,但是如果该方法报异常,会被抓住。
10                     //不会影响程序正常运行,只会打印出来
11                     interceptor.afterCompletion(request, response, this.handler, ex);
12                 }
13                 catch (Throwable ex2) {
14                     logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
15                 }
16             }
17         }
18     }

下面这个就是打印了一下,但是不会影响我们的请求响应回去:

 还是会正常响应回客户端:

 好了,到此拦截器的实现以及源码分析流程到此结束,本来想给大家从Springboot的reflash方法开始解析拦截器,但是内容太多了,不仅跑题而且博主也一时半会给大家无法讲解明白。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/34250.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity学习笔记---调试

使用Log进行调试 使用Debug.Log方法可以将一些运行时信息打印到Console窗口中。 打印时间戳 //获取时间 Debug.Log(DateTime.Now.ToString());//打印毫秒级的时间 Debug.Log(((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000) * 0.001); 打印自定义文…

Hive基础教程

文章目录 Apache Hive 教程1. Hive-简介1.1 学习Hive的前置知识1.2 什么是Hive&#xff1f;1.3 Hive的架构1.4 Hive的工作流程 Apache Hive 教程 资料来源&#xff1a;Hive Tutorial (tutorialspoint.com) Hive是Hadoop中用于处理结构化数据的数据仓库基础设施工具。它驻留在H…

Pytorch加载模型

一、使用torchvision加载模型 1.使用说明: torchvision是PyTorch生态系统中的一个包&#xff0c;专门用于计算机视觉任务。它提供了一系列用于加载、处理和预处理图像和视频数据的工具&#xff0c;以及常用的计算机视觉模型。 torchvision.models模块包含许多常用的预训练计…

【IEEE独立出版、有确定的ISBN号】第三届能源与电力系统国际学术会议 (ICEEPS 2024)

第三届能源与电力系统国际学术会议 (ICEEPS 2024) 2024 3rd International Conference on Energy and Electrical Power Systems 连续2届会后4-5个月EI检索&#xff0c;检索稳定&#xff01; 成功申请IEEE出版&#xff01; 特邀院士、Fellow 报告&#xff01; 一、大会信息 …

关于sum+=1与sum=sum+1的关系(C语言)

一、sum 1;与sum sum 1;是相等的&#xff0c;运算结果相等&#xff1b; 二、用一段代码说明&#xff1b; # define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int main() {//初始变量值&#xff1b;int n1 1;int n2 1;//输出&#xff1b;printf("运算前的n…

防止在 Qt 中触发信号

在 Qt 中工作时&#xff0c;有时我们需要暂时阻止某些信号的触发。以下是一个经典场景&#xff1a;我们有一个 QCheckBox 对象&#xff0c;当用户勾选或取消勾选时&#xff0c;需要调用一个函数&#xff0c;因此我们将这个函数连接到 stateChanged(int state) 信号。然而&#…

华媒舍:5种中国台湾媒体发稿套餐推广策略大汇聚!

在当代商业时代中&#xff0c;推广策略针对企业的成功尤为重要。中国台湾作为一个经济发展发达的地区&#xff0c;有着繁盛新闻媒体产业链&#xff0c;为企业提供了各种各样发稿套餐来推广其产品与服务。下面我们就详细介绍5种中国台湾媒体发稿套餐推广策略&#xff0c;助力企业…

仓库管理系统03--设计登录页面

1、添加登录窗体 2、整体布局UI 1&#xff09;设计三个白底的边框&#xff0c;其中2个旋转角度&#xff0c;形成以下效果 3、设计登录控件 <Window x:Class"West.StoreMgr.Windows.LoginWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presenta…

【Windows】“ONLYOFFICE 桌面编辑器 8.1 新特性大揭秘:全新 PDF 编辑器、幻灯片版式和更多更新“

【Windows】“ONLYOFFICE 桌面编辑器 8.1 新特性大揭秘&#xff1a;全新 PDF 编辑器、幻灯片版式和更多更新” 大家好 我是寸铁&#x1f44a; 【Windows】“ONLYOFFICE 桌面编辑器 8.1 新特性大揭秘&#xff1a;全新 PDF 编辑器、幻灯片版式和更多更新”✨ 喜欢的小伙伴可以点点…

精准时刻,掌控未来:解锁新一代PCIe时钟板卡!

在当今高度互联和精确度要求极高的技术环境中&#xff0c;时间同步与精确计时显得尤为重要。为了满足行业对高精度时间管理的不断增长的需求&#xff0c;我们自豪地推出铷原子时钟板卡和高精度时钟同步板卡。这两款创新板卡结合了先进的技术和卓越的性能&#xff0c;为各类应用…

TCP/IP 在 Linux 内核中的实现

之前出了一个python的socket编程的文章&#xff0c;里面讲的是怎么进行socket编程。最近想到TCP/IP协议的原理&#xff0c;然后查阅资料后说是在操作系统级别实现的&#xff0c;python的socket模块只是一个接口。 本文就来谈一下Linux源码里实现TCP/IP协议簇的源代码在哪里&am…

C++ 图像处理

OpenCV提供了丰富的图片处理功能。以下是一些常见的图片处理方式的C示例代码&#xff1a; #include <opencv2/opencv.hpp> #include <iostream> int main() { // 读取图片 cv::Mat img cv::imread("example.jpg"); // 检查图片是否成功读取 if …

域名注册成功后能删除吗?

域名注册成功后&#xff0c;很多用户可能会有这样的疑问&#xff1a;如果不再需要这个域名&#xff0c;能否将其删除?这个问题的答案取决于多个因素&#xff0c;包括域名的注册政策、使用期限以及注册商的规定。本文将探讨域名注册成功后的删除问题&#xff0c;帮助用户更好地…

巧用SOLIDWORKS进行复杂曲面模型的实体建模

SOLIDWORKS正版软件有实体建模、曲面建模、钣金建模、焊件建模、模具建模等多种建模方式&#xff0c;每种建模方式都有独特的使用环境和场景&#xff0c;如果能灵活使用这些建模命令&#xff0c;就可以起到事半功倍的效果。 如上图所示的模型&#xff0c;通过观察和分析&#x…

1992-2023年全国及31省人均地区生产总值/人均GDP数据(无缺失)

1992-2023年全国及31省人均地区生产总值/人均GDP数据&#xff08;无缺失&#xff09; 1、时间&#xff1a;1992-2023年 2、来源&#xff1a;国家统计局、各省年鉴 3、指标&#xff1a;人均国内生产总值/人均GDP 4、范围&#xff1a;全国及31省 5、缺失情况&#xff1a;无缺…

中介子方程三十一

XXFXXuXXWXXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXdXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXdXXpXuXXαXXrXXEXXyXXαXiXXαXiXrXkXtXyXXpXVXXdXuXWX…

什么是测试自动化?意义、方法、方法论、工具和收益

测试自动化使用软件和脚本来自动测试程序&#xff0c;使测试更快、更有效。本文将了解测试自动化的基本原理、方法、工具、好处和挑战。 测试自动化被定义为软件开发中用于自动执行测试的技术。 它包括编写脚本或使用专门的软件工具来自动执行测试&#xff0c;而无需人工干预。…

【杂记-浅谈LSA链路状态通告】

浅谈LSA链路状态通告 一、LSA链路状态通告概述二、LSA的作用三、LSA的类型和使用场景 一、LSA链路状态通告概述 LSA&#xff0c;Link State Advertisement&#xff0c;链路状态通告&#xff0c;是OSPF协议中的一个核心组件&#xff0c;用于在路由器之间交换链路状态信息。LSA包…

Spring Boot 和 Spring Framework 的区别是什么?

SpringFramework和SpringBoot都是为了解决在Java开发过程中遇到的各种问题而出现的。了解它们之间的差异&#xff0c;能够更好的帮助我们使用它们。 SpringFramework SpringFramework是一个开源的Java平台&#xff0c;它提供了一种全面的架构和基础设施来支持Java应用程序的开…

ffmpeg的安装教程

1.官网下载ffmpeg 进入Download FFmpeg网址&#xff0c;点击下载windows版ffmpeg&#xff08;点击左下第一个绿色的行&#xff09; 在release builds第一个绿框里面选择一个版本下载。 2.配置 下载完成后解压该压缩包单击进入ffmpeg\bin&#xff0c;会出现如下界面&#xff1…