当前位置: 首页 > news >正文

【记录版】SpringBoot下Filter注册源码解读

SpringBoot + TomcatEmbeddedContext + Servlet + ApplicationFilterChain + Filter

背景: 在之前博客中有说明SpringBoot内嵌Web容器后,Filter及Servlet解析与注册流程的变化。将Filter实例封装成FilterRegistrationBean实例并添加到ServletContext后,到实际使用Filter完成过滤功能之前,其实中间还有一些管理流程。本文将在此背景下,继续追加描述此块逻辑。

博客内容精选:
1、Servlet请求体重复读&修改新姿势
2、根据请求获取后端接口详情
3、SpringBoot下Filter自动适配
4、Filter链式执行设计解读

一、首
先Filter相关定义经解析过存储在TomcatEmbeddedContext对象中,有三个重要变量:
1、private Map<String, FilterDef> filterDefs // 存储Filter定义元数据,如filterClass、name等
2、private final StandardContext.ContextFilterMaps filterMaps // FilterMap-匹配元数据,如String[] urlPatterns
3、private Map<String, ApplicationFilterConfig> filterConfigs // 由filterDefs可封装成ApplicationFilterConfig,核心为Filter实例

ApplicationFilterConfig(Context context, FilterDef filterDef) throws Exception{this.context = context;this.filterDef = filterDef;if (filterDef.getFilter() == null) {this.getFilter();} else {this.filter = filterDef.getFilter();context.getInstanceManager().newInstance(this.filter);this.initFilter();}
}

我们都知道Filter要发布到Servlet容器,先以FilterRegistrationBean的形式封装,因注册Bean底层接口包含ServletContextInitializer,其在onStartup(ServletContext servletContext)方法执行下注册到ServletContext中去,以下为注册代码:

// 执行入口
public final void onStartup(ServletContext servletContext) throws ServletException {..........省略.............this.register(description, servletContext);
}
// 注册入口
protected final void register(String description, ServletContext servletContext) {// 将Filter实例加入ContextD registration = this.addRegistration(description, servletContext);if (registration == null) {logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");} else {// 配置Filter匹配元数据this.configure(registration);}
}
// Filter实例对象注册
protected Dynamic addRegistration(String description, ServletContext servletContext) {Filter filter = this.getFilter();// 此处可将Filter实例添加到FilterDef =》filterDef.setFilter(filter)// 返回ApplicationFilterRegistration(filterDef, this.context)对象return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}
// URL等匹配元数据注册
protected void configure(Dynamic registration) {super.configure(registration);EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;..........省略..........// 重点registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, StringUtils.toStringArray(this.urlPatterns));
}public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... urlPatterns) {FilterMap filterMap = new FilterMap();filterMap.setFilterName(this.filterDef.getFilterName());..........if (urlPatterns != null) {for(int var7 = 0; var7 < urlPatterns.length; ++var7) {String urlPattern = urlPatterns[var7];filterMap.addURLPattern(urlPattern);}if (isMatchAfter) {// 此处即为添加匹配元数据到Servlet上下文中this.context.addFilterMap(filterMap);} else {this.context.addFilterMapBefore(filterMap);}}
}

二、ApplicationFilterFactory使用createFilterChain工厂方法创建ApplicationFilterChain链
createFilterChain有两大核心逻辑
1)创建ApplicationFilterChain实例

ApplicationFilterChain filterChain = null;
if (request instanceof Request) {Request req = (Request)request;if (Globals.IS_SECURITY_ENABLED) {filterChain = new ApplicationFilterChain();} else {filterChain = (ApplicationFilterChain)req.getFilterChain();if (filterChain == null) {// 核心逻辑,直接创建Chain实例,且构造器无其他初始化逻辑filterChain = new ApplicationFilterChain();// chain对象复用,注意这里req对象是org.apache.catalina.connector.Request// chain内容不复用,filterChain使用完毕会调用release方法释放资源req.setFilterChain(filterChain);}}
} else {filterChain = new ApplicationFilterChain();
}

2)过滤器链初始化【极简版】

// Servlet设置
filterChain.setServlet(servlet); // DispatcherServlet实例
StandardContext context = (StandardContext)wrapper.getParent(); // TomcatEmbeddedContext
// 获取Servlet上下文中的过滤器匹配元数据
FilterMap[] filterMaps = context.findFilterMaps();
if (filterMaps != null && filterMaps.length != 0) {// 分发类型,一般为RequestDispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");// 请求URI解析,此处忽略   String requestPath = xxx;String servletName = wrapper.getName(); // 值为dispatcherServletFilterMap filterMap;ApplicationFilterConfig filterConfig;for(int index = 0; index < filterMaps.length; ++index) {filterMap = filterMaps[index];// 目前Springboot项目基本只需解析matchFiltersURL方法,根据URL匹配对应Filterif (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());if (filterConfig != null) {// 过滤器链中数组对象存的是ApplicationFilterConfig实例,而不是Filter实例,因为部分场景需要元数据作为判断条件filterChain.addFilter(filterConfig);}}}// Servlet匹配Filter当前无用,仅作参考。结构类似,代码忽略..............................return filterChain;
}

三、过滤器链执行入口:StandardWrapperValve
在StandardWrapperValve中,由invoke(Request request, Response response)方法内执行过滤器链,StandardWrapperValve前也是通过责任链模式一步步传递过来,也有相似的Valve实例。

过滤器链执行流程很简单,可以简化为以下过程:
// 注:这里传给Filter的其实是RequestFacade对象,包装了底层的Request对象,Response同理
1、filterChain.doFilter(request.getRequest(), response.getResponse())

2、中间过滤器链执行流程,已在其他博客说明

//过滤器链每次执行后,释放资源
3、filterChain.release()

总结:本文将Filter在Servlet上下文的注册流程、FilterChain过滤器链的封装流程基本讲述完成,其他Filter相关知识可参考之前其他博客内容,剩余细节不在展开。

思考:
1、过滤器池化相关概念,实际不是FilterChain的池化,其每次会recycle,而是org.apache.coyote.Request请求对象的池化(注意:此对象不是实现HttpServletRequest的org.apache.catalina.connector.Request)。Request对象较为底层且复杂,在并发量较低时,同一接口间隔请求都是一个Request对象,使用完成也会调用recycle回收,response同理。
2、FilterChain在Request池化基础上,每次不会新建,但是每次请求都会重新生成ApplicationFilterConfig数组。按现在Springboot请求流程,暂不清楚为啥需要recycle,(只是元数据封装,性能影响很小)可能考虑到Filter可能在服务期间被destroy?
3、至于线程安全问题,FilterChain是多例的,Filter是单例的,但是被多个FilterChain封装,也能并发执行,所以不要在Filter中定义共享变量。

相关文章:

【记录版】SpringBoot下Filter注册源码解读

SpringBoot TomcatEmbeddedContext Servlet ApplicationFilterChain Filter 背景&#xff1a; 在之前博客中有说明SpringBoot内嵌Web容器后&#xff0c;Filter及Servlet解析与注册流程的变化。将Filter实例封装成FilterRegistrationBean实例并添加到ServletContext后&…...

WPF的WebBrowser控件

在 WPF 中显示网页&#xff0c;你可以使用 WebBrowser 控件来实现。WebBrowser 控件是一个嵌入式的浏览器控件&#xff0c;可以加载和显示网页内容。 以下是在 WPF 中显示网页的示例代码&#xff1a; <Window x:Class"WpfApp.MainWindow"xmlns"http://sche…...

WX小程序案例(一):弹幕列表

WXML内容 <!--pages/formCase/formCase.wxml--> <!-- <text>pages/formCase/formCase.wxml</text> --> <view class"bk bkimg"><!-- <image src"/static/imgs/ceeb653ely1g9na2k0k6ug206o06oaa8.gif" mode"scal…...

基于ssm医用物理学实验考核系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本医用物理学实验考核系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数…...

鸿蒙HarmonyOS4.0 入门与实战

一、开发准备: 熟悉鸿蒙官网安装DevEco Studio熟悉鸿蒙官网 HarmonyOS应用开发官网 - 华为HarmonyOS打造全场景新服务 应用设计相关资源: 开发相关资源: 例如开发工具 DevEco Studio 的下载 应用发布: 开发文档:...

论文阅读——GroupViT

GroupViT: Semantic Segmentation Emerges from Text Supervision 一、思想 把Transformer层分为多个组阶段grouping stages&#xff0c;每个stage通过自注意力机制学习一组tokens&#xff0c;然后使用学习到的组tokens通过分组模块Grouping Block融合相似的图片tokens。通过这…...

时光机器:用rrweb打造可回溯的用户体验!

在现代Web应用中&#xff0c;理解用户如何与你的产品互动变得越来越重要。rrweb&#xff08;record and replay the web&#xff09;是一个开源库&#xff0c;它能够记录用户在网站上的所有操作&#xff0c;并能够像回放视频一样回放这些操作。这就像给你的网站装上了一台时光机…...

不同的葡萄品种的葡萄酒有什么共同特质?

在某种程度上几乎所有的葡萄酒都是混合的&#xff0c;在大多数葡萄酒产地&#xff0c;法律允许在单一品种葡萄酒中混入高达15%的另一种葡萄酒&#xff0c;且还能被称为由主要葡萄酿造的单一品种葡萄酒酒。这些单一品种葡萄酒混合了少量其他葡萄酒&#xff0c;是为了创造一个特质…...

Visual Studio编辑器中C4996 ‘scanf‘: This function or variable may be unsafe.问题解决方案

目录 ​编辑 题目&#xff1a;简单的ab 1. 题目描述 2. 输入格式 3. 输出格式 4. 样例输入 5. 样例输出 6. 解题思路 7. 代码示例 8. 报错解决 方案一 方案二 方案三 方案四 总结 题目&#xff1a;简单的ab 1. 题目描述 输入两个整数a和b&#xff0c;…...

C与C++编程语言的区别和联系

一、引言 C和C是两种广泛使用的编程语言&#xff0c;它们都在软件开发领域有着广泛的应用。虽然C是从C语言演化而来的&#xff0c;但两者之间存在一些重要的区别和联系。本文将详细介绍这两种编程语言的相同点和不同点&#xff0c;并通过实际例子进行说明。 二、C与C的相同点 …...

UE4 UMG 颜色字体和PS对应关系

与PS中对应的是Hex sRGB色号 但是PS中采用的16进制色号为6位 UE4中的为8位 UMG制作时默认dpi为96像素/英寸&#xff0c;psd默认dpi是72像素/英寸。 在GUI设计时将dpi设为96&#xff0c;或者将72dpi下字体的字号乘以0.75&#xff0c;都能还原效果图中的效果。...

EasyExcel处理表头的缓存设置

在学习EasyExcel 时会发现针对使用类模型配置表头相关属性时&#xff0c;EasyExcel 会使用到缓存技术以提升表头的解析速度如下代码&#xff1a; 这些参数再何时设置的哪&#xff1f; 在easyExcel 基础参数设置中会有这个参数filedCacheLocation 。默认采用的使用线程级别的…...

数据挖掘任务一般流程

数据挖掘是从大量数据中提取有价值信息的过程。它涉及多个步骤&#xff0c;每一步都对整个数据挖掘过程至关重要。以下是数据挖掘任务的一般流程&#xff1a; 业务理解&#xff1a; 确定业务目标。评估当前情况。定义数据挖掘问题。制定一个初步计划来达到这些目标。 数据理…...

人工智能计算机视觉:解析现状与未来趋势

导言 随着人工智能的迅速发展&#xff0c;计算机视觉技术逐渐成为引领创新的关键领域。本文将深入探讨人工智能在计算机视觉方面的最新进展、关键挑战以及未来可能的趋势。 1. 简介 计算机视觉是人工智能的一个重要分支&#xff0c;其目标是使机器具备类似于人类视觉的能力。这…...

5.1 C++11强类型枚举

一、C枚举的缺陷 1.类型冲突 枚举值和类型都是全局可见的&#xff0c; 与正常C的namespace、类等都是格格不入的&#xff0c;并且还容易导致冲突。 enum Type { General, Light, Medium, Heavy }; enum Category { General, Pistol, MachineGun, Cannon }; 如果在相同作用域…...

Android : BottomNavigation底部导航_简单应用

示例图&#xff1a; 1.先创建底部导航需要的图片 res → New → Vector Asset 创建三个矢量图 图片1 baseline_home.xml <vector android:height"24dp" android:tint"#000000"android:viewportHeight"24" android:viewportWidth"24…...

基于ssm培训学校教学管理平台论文

摘 要 社会的进步&#xff0c;教育行业发展迅速&#xff0c;人们对教育越来越重视&#xff0c;在当今网络普及的情况下&#xff0c;教学管理模式也开始逐渐网络化&#xff0c;学校开始网络教学管理模式。 本文研究的培训学校教学管理平台基于SSM框架&#xff0c;采用Java技术和…...

关于嵌入式开发的一些信息汇总:C标准、芯片架构、编译器、MISRA-C

关于嵌入式开发的一些信息汇总&#xff1a;C标准、芯片架构、编译器、MISRA-C 关于C标准芯片架构是什么&#xff1f;架构对芯片有什么作用&#xff1f;arm架构X86架构mips架构小结 编译器LLVM是什么&#xff1f;前端在干什么&#xff1f;后端在干什么&#xff1f; MISRA C的诞生…...

java实现局域网内视频投屏播放(二)爬虫

代码链接 视频播放原理 大多视频网站使用的是m3u8&#xff0c;m3u8其实不是一个真正的视频文件&#xff0c;而是一个视频播放列表&#xff08;playlist&#xff09;。它是一种文本文件&#xff0c;里面记录了一系列的视频片段&#xff08;segment&#xff09;的网络地址。这些…...

a标签的target属性

<a> 标签的 target 属性规定在何处打开链接文档。 最常用的两个值是&#xff1a; _self &#xff1a; 在当前窗口打开被链接文档 _blank&#xff1a;在新窗口打开被链接文档 就是常见浏览网页打开链接的方式...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

二维FDTD算法仿真

二维FDTD算法仿真&#xff0c;并带完全匹配层&#xff0c;输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

OpenGL-什么是软OpenGL/软渲染/软光栅?

‌软OpenGL&#xff08;Software OpenGL&#xff09;‌或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式&#xff08;包括几何处理、光栅化、着色等&#xff09;&#xff0c;不依赖GPU硬件加速。这种模式通常性能较低&#xff0c;但兼容性极强&#xff0c;常用于不支持硬件加速…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器

从本章节开始&#xff0c;进入到函数有多个参数的情况&#xff0c;前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参&#xff0c;ECX是整型的第一个参数的寄存器&#xff0c;那么多个参数的情况下函数如何传参&#xff0c;下面展开介绍参数为整型时候的几种情…...