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.rhkb.cn/news/358114.html

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

相关文章

H4020 12V24V36V40V1A 同步降压芯片IC Buck-DCDC 低功耗,高效率 100%占空比

H4020是一款12V24V36V40V1A的同步降压&#xff08;Buck&#xff09;DC-DC转换器&#xff0c;专为需要高效率、低功耗和精确电压/电流控制的应用而设计。它内置了高压MOSFET&#xff0c;支持宽范围的输入电压&#xff08;5V-36V&#xff09;&#xff0c;并能提供高达1A的持续输出…

汇编快速入门

一.基础知识 1.数据类型 DB&#xff08;Define Byte&#xff0c;字节类型 占位8位bit 1字节&#xff09; 范围&#xff1a;DB可以用来定义&#xff08;无符号、有符号&#xff09;整数&#xff08;包含二、十、十六进制&#xff09;和字符 语法&#xff1a;a DB 数据个数…

“人工智能+”带来新变化

以生成式人工智能&#xff08;AIGC&#xff09;为代表的新一代人工智能技术创新加速演进&#xff0c;相关商业化应用成果也不断涌现&#xff0c;行业应用范围不断拓展&#xff0c;深度赋能实体经济&#xff0c;为行业提质增效与实现减排提供助力。 自主航运初创公司OrcaAI于6月…

g++制作C++动态库的简洁例程

g制作C动态库的简洁例程 code review! 文章目录 g\制作C动态库的简洁例程1. 创建 C 动态库1.1 动态库源文件1.2 编译动态库 2. 使用动态库2.1 命令行编译链接然后运行2.2 使用 CMake 编译链接然后运行 3.附加笔记&#xff1a;关于运行时是否能找到libmylib.so的问题汇总3.1.g -…

STM32项目分享:智能窗帘系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…

浅析缓存技术

缓存技术的原理 缓存技术通过在内存中存储数据副本来加速数据访问。当应用程序需要数据时&#xff0c;首先检查缓存是否存在数据副本&#xff0c;如果有则直接返回&#xff0c;否则再从原始数据源获取。这种机制大大减少了访问时间&#xff0c;提升了系统的响应速度和整体性能。…

谷歌手机刷机教学

注意&#xff1a;手机已经解开了oem锁和bl 1、adb基础命令 连接设备adb devices&#xff1a;列出当前连接的所有设备。 adb connect <设备IP>&#xff1a;通过IP地址连接设备&#xff08;用于无线连接&#xff09;。 设备信息adb shell getprop&#xff1a;获取设备的所…

Linux使用——查看发行版本、内核、shell类型等基本命令

先做快照 虚拟机中编辑网络 关机 普通账户和管理员账户 互相对照 localhost相当于IP 参数: 短格式:以减号(-)开头&#xff0c;参数字母 长格式:以2个减号(--)后跟上完整的参数单词 当前发行版本 [rootserver ~]# cat /etc/redhat-release Red Hat Enterprise Linux release 9.…

Langchain实战:构建高效的知识问答系统

引言 知识问答系统&#xff08;KQA&#xff09;是自然语言处理领域的核心技术之一&#xff0c;它能够帮助用户从大量数据中快速准确地检索到所需信息。知识问答系统成为了帮助个人和企业快速获取、筛选和处理信息的重要工具。它们在很多领域都发挥着重要作用&#xff0c;例如在…

每日一练:攻防世界:5-1 MulTzor

一、XorTool 基于 XOR&#xff08;异或&#xff09;运算实现。它可以帮助您快速地对文本、二进制文件进行加密解密操作。 认识XorTool工具&#xff1a; 让我们先去认识一下工具&#xff1a; xortool.py 是基于 python 的脚本&#xff0c;用于完成一些 xor 分析&#xff0c;…

TCP与UDP_三次握手_四次挥手

TCP vs UDP TCP数据 具体可以通过Cisco Packet Tracer工具查看&#xff1a; UDP数据 三次握手、四次挥手 为什么是3/4次&#xff1f;这牵扯到单工、双工通信的问题 TCP建立连接&#xff1a;表白 TCP释放连接&#xff1a;分手 TCP—建立连接—三次握手 解释&#xff1a; 首先&…

CSS+JS:通过修改filter实现图片颜色随时间渐变

原理&#xff1a;修改filter的hue-rotate属性 效果&#xff1a; 代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…

7.系统工具——黑马程序员Java最新AI+若依框架项目

目录 前言一、表单构建任务&#xff1a;设计添加课程表单 二、 代码生成1.任务&#xff1a;将部门表在页面端显示改为树形结构 三、系统接口任务&#xff1a;使用sagger进行接口测试 前言 提示&#xff1a;本篇讲解若依框架 系统工具 一、表单构建 功能&#xff1a;完成前端…

《web程序设计》课程大作业,XX地旅游景点网站【IDEA下JSP(前后端)+MySQL技术】

背景&#xff1a; 《web程序设计》课程大作业要求 一、课程目标&#xff1a;课程教学目的是让学生能够全面了解和掌握目前国内比较流行的交互式网页制作的理论知识与开发技术&#xff0c;能开发制作出有一定实用性的交互式网站&#xff0c;为将来继续学习和就业打下坚实基础。…

Linux系统及常用命令介绍

一.介绍 Linux一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个遵循POSIX的多用户、多任务、支持多线程和多CPU的操作系统。Linux系统的说明可以自行百度&#xff0c;知道这几点即可&#xff1a; 1.Linux中一切都是文件&#xff1b; 2.Linux是一款免费操作系统&…

水滴型粉碎机:饲料加工关键设备

在现代饲料加工行业中&#xff0c;高效、精准的加工设备对于提升饲料品质和产量至关重要。水滴型粉碎机作为一种实用的饲料加工设备&#xff0c;凭借其设计和性能&#xff0c;在饲料加工领域发挥着不可替代的作用。 一、水滴型粉碎机的设计特点 水滴型粉碎机采用了水滴型设计&a…

[图解]企业应用架构模式2024新译本讲解16-行数据入口2

1 00:00:00,750 --> 00:00:02,470 好&#xff0c;我们来看代码 2 00:00:03,430 --> 00:00:06,070 我们一步一步执行 3 00:00:42,500 --> 00:00:45,000 先初始化数据 4 00:00:52,300 --> 00:00:53,650 创建连接 5 00:00:55,900 --> 00:00:56,970 这里面 6 0…

帝国cms批量取消文章审核-把已审核的文章改成未审核的方法

帝国cms很多人采集的时候&#xff0c;把文章弄成了审核过的文章&#xff0c;或者因为其他的原因&#xff0c;文章都是审核通过&#xff0c;为了seo又不能把全部文章放出来&#xff0c;所以需要把文章弄成未审核以下就是解决本问题的办法 首先来修改后台列表文件&#xff0c;自…

【SPSS】基于RFM+Kmeans的电商客户价值聚类分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Node.js 是一个开源的 跨平台的JavaScript运行环境

https://www.npmjs.com/ 中央仓库 Visual Studio Code - Code Editing. Redefined https://openjsf.org/ OpenJS 促进了关键 JavaScript 技术在全球范围内的广泛采用和持续发展。 Apache服务器 Nginx服务器 Tomcat服务器 Node.js服务器 Gunicorn服务器 uW…