Servlet-Filter 执行顺序测试

Servlet-Filter 执行顺序测试

对于 web.xml 文件注册过滤器这里就不多说了,就是谁声明的早,谁先被调用。因为在上面的过滤器信息最先被扫描到。

模型抽象

为了便于在实践中使用,结合部分底层原理,我们可以对 Filter 链的执行做一下抽象。

我们有一个初始容量为 0 的队列,该队列中有一个 insertPos(以下简写为 ips,初始值为-1) 的属性,用来指示上一个 isMatchAfter 值为 false 的 filter 的添加位置(下标)。当尝试 addMappingForUrlPattern 时,判断当前 Filter 的 isMatchAfter 的属性值:

  • 若 isMatchAfter 值为 true,则直接将元素添加至队尾;
  • 若 isMatchAfter 值为 false,则将该元素添加到下标为 insertPos + 1 的位置,即插入到上一个 isMatchAfter 为 false 的 filter 的下一个位置,随后将 ips ++;

我们举例说明这个模型的使用:
true, true, false, false 为例,那么假如的顺序为:

  • 加入链尾。01, ips 为 -1;
  • 加入链尾。01 -> 02,ips 为 -1;
  • 加入到 ips + 1 = 0 的位置。03 -> 01 -> 02,ips 为 0;
  • 加入到 ips + 1 = 1 位置。03 -> 04 -> 01 -> 02,ips 为 1;

若顺序为 false, false, true, true

  • 加入 ips + 1 = 0的位置。01,ips 为 0;
  • 加入到 ips + 1 = 1 位置。01 -> 02,ips 为 1;
  • 加入链尾。01 -> 02 -> 03
  • 加入链尾。01 -> 02 -> 03 -> 04

所以按照模型,在 web.xml 中注册的 filter 默认 isMatchAfter 的值就全部为 true 或者 false。

测试环境

  • tomcat 10.1.17
  • servlet-api: Jakarta-servlet-api: 6.0.0, Jakarta-annotaion: 2.0.0
  • Java 17

使用 @WebFilter 注解

结论

如果使用注解,那么 Filter 的顺序就取决于文件的组织形式,即按照文件夹的排列的顺序来定义 filter 的顺序。

而 Idea 的文件的组织形式都是按照文件名进行排序的,所以如果用模型来解释,以这种方式注册的 filter,isMatchAfter 的值都为 true 或者 false。谁先被扫描到,谁就先加入队列。

过程

同一文件夹内

若文件的组织如下图所示:
image.png

@WebFilter(value = "/*", filterName = "03")
@Slf4j
public class _01 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("01 filter....");
        chain.doFilter(request, response);
    }
}


@WebFilter(value = "/*", filterName = "02")
@Slf4j
public class _02 implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("02 filter....");
        chain.doFilter(request, response);
    }
}

@Slf4j
@WebFilter(value = "/*", filterName = "01")
public class _03 implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("03 filter....");
        chain.doFilter(request, response);
    }
}

执行结果为(经过多次请求地址 localhost:8080,仍然为这一结果):

2023-12-22 11:29:22.871 INFO  [http-nio-8080-exec-1]  c.y.w.fiter._01 01 filter....
2023-12-22 11:29:22.874 INFO  [http-nio-8080-exec-1]  c.y.w.fiter._02 02 filter....
2023-12-22 11:29:22.874 INFO  [http-nio-8080-exec-1]  c.y.w.fiter._03 03 filter....
2023-12-22 11:29:23.516 INFO  [http-nio-8080-exec-2]  c.y.w.fiter._01 01 filter....
2023-12-22 11:29:23.516 INFO  [http-nio-8080-exec-2]  c.y.w.fiter._02 02 filter....
2023-12-22 11:29:23.516 INFO  [http-nio-8080-exec-2]  c.y.w.fiter._03 03 filter....
2023-12-22 11:29:23.732 INFO  [http-nio-8080-exec-3]  c.y.w.fiter._01 01 filter....
2023-12-22 11:29:23.732 INFO  [http-nio-8080-exec-3]  c.y.w.fiter._02 02 filter....
2023-12-22 11:29:23.732 INFO  [http-nio-8080-exec-3]  c.y.w.fiter._03 03 filter....


不同文件夹下

文件夹的组织如下:
image.png

类信息不变。

执行结果为:
image.png


如果我们把类名进行修改,但是类信息不变,如下:
image.png

执行结果为:
image.png

可以看到,仍然是按照从上到下的顺序进行执行的,所以,由以上实验可以得到 WebFilter 定义的注解与类名和 filterName 属性都无关。

总结

可见,在 idea 测试环境下,若把所有的 Filter 组织在一个文件夹下,那么 Filter 的执行顺序是与类名有关的。因为,idea 的文件组织是默认按照名称进行排序的(与 windows 的默认文件排序方式无关)。

也就是说,过滤器的执行顺序只与谁先被扫描到(谁先被加入到过滤器链条)有关(Servlet-api 底层的定义的是 LinkedHashSet 结构来存储过滤器链的)。所以,我们不推荐使用注解的方法定义过滤器链

ServletContext 动态注册 Filter

对于这种情况,网上有很多种说法,最可信的(看起来对的)说法是,按照加入 ServletContext 的顺序为执行顺序,这种说法是很有道理的。因为不论是扫描 xml 文件加入 Filter 还是扫描文件夹假如 Filter,最后都将这个过滤链加入了 ServletContext。

倘若我们嫌分析源码麻烦,那么做一个实验去验证是最为经济的做法了。

我们对上次的实验的类进行保留,只是去除 WebFilter 注解。然后尝试进行动态注册。

结论

  • addMappingForUrlPatterns 方法的调用决定了过滤器的顺序:
    • isMatchAfter 全部为 true 时,方法的调用顺序决定了过滤器的执行顺序,并且为正相关(先调用先执行);
    • isMatchAfter 全部为 false 时,与上一种情况相同;
    • 当有一个 filter 的 isMatchAfter 属性为 true 或者 false 的时候,

不同文件夹下

image.png

对照实验

我们设置的测试代码为:

@Slf4j
@WebListener
public class ContainerInitializer implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        FilterRegistration.Dynamic filter_01 = ctx.addFilter("01", _01.class);
        FilterRegistration.Dynamic filter_02 = ctx.addFilter("02", _02.class);
        FilterRegistration.Dynamic filter_03 = ctx.addFilter("03", _03.class);
        filter_01.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        filter_02.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        filter_03.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
    }
}

最后的执行结果为,重复四次实验(访问 localhost:8080):

可以看到,顺序为我们注册到 ServletContext 的顺序。请忽略后续的 log 的 message 信息,这是无关紧要的。


改变 addFilter 顺序

现在我们调换 addFilter 的顺序,从 _01 -> _02 -> _03 调整为 _02 -> _01 -> _03。其余不变,即:

		FilterRegistration.Dynamic filter_02 = ctx.addFilter("02", _02.class);
        FilterRegistration.Dynamic filter_01 = ctx.addFilter("01", _01.class);
        FilterRegistration.Dynamic filter_03 = ctx.addFilter("03", _03.class);
        // nothing changed
        

执行结果为:
image.png

发现结果不变,说明改变,addFilter 顺序行为并不能改变注册顺序。


改变 filterName

尝试改变 filterName 来试图调整顺序,即:

		FilterRegistration.Dynamic filter_01 = ctx.addFilter("03", _01.class);
        FilterRegistration.Dynamic filter_02 = ctx.addFilter("02", _02.class);
        FilterRegistration.Dynamic filter_03 = ctx.addFilter("01", _03.class);

执行结果为:
image.png

没有任何效果。


改变 addMappingForUrlPatterns 的调用
isMatchAfter 全部为 true

尝试改变 addMappingForUrlPatterns 方法的调用顺序,即:

        filter_02.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        filter_01.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
        filter_03.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");

执行结果为:
image.png
不受到 isMatchAfter 的影响。只与调用顺序有关。

isMatchAfter 全部为 false

直接上结果:
image.png
不受到 isMatchAfter 的影响。只与调用顺序有关。

isMatchAfter 只有一个为 true

image.png

执行结果为:
image.png
为 true 的 filter 在所有 filter 之后执行。

isMatchAfter 只有一个为 false

修改源码为:
image.png

执行结果为:
image.png
可以看见,isMatchAfter 为 true 的 filter 变成了第一个执行的。

当有多个 true 和 false 时

为了测试方便,我们新增一个 filter,对代码做如下修改:
image.png

执行结果为:
image.png


调换顺序:
image.png

执行结果为:
image.png


调换顺序:
image.png

执行结果为:
image.png

全部能够执行,并且按照调用顺序执行。根据以上,规律可以进行总结了。

若一个 filter 在注册的时候,将 isMatchAfter 设置为 false,就会调换到过滤链中所有 isMatchAfter 值为 false 的下一个位置(不加入到队首);若设置为 true,就默认加载过滤链尾。

故而,在多个 true,false 混合的时候,例如以 true, true, false, false 为例,那么假如的顺序为:

  • 加入链尾。01
  • 加入链尾。01 -> 02
  • 加入链首。03 -> 01 -> 02
  • 加入链首。03 -> 04 -> 01 -> 02

若顺序为 false, false, true, true

  • 加入链首。01
  • 加入链首。01 -> 02
  • 加入链尾。01 -> 02 -> 03
  • 加入链尾。01 -> 02 -> 03 -> 04

这与我们的实验结果是一致的。

总结

在不同文件夹下,过滤器的调用顺序与结论符合。

同一文件夹下

实验结果完全相同。这里的实验过程,限于篇幅就略过了。

总结

在同一文件夹下,过滤器的调用顺序与结论符合。

总结

动态注册过滤器的执行顺序只与 addMappingForUrlPatterns 方法的调用顺序和该方法的参数 isMatchAfter 有关。与文件夹,文件名,filterName 无关。

总结

实验结果完全契合模型。实际上,结合 tomcat 的源码,tomcat 只不过是将模型的队列用数组实现了而已。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.net core 生成jwt+swagger-通过 IHttpContextAccessor读取token信息

1.安装jwt相关包 <ItemGroup><PackageReference Include"Microsoft.AspNetCore.Authentication.JwtBearer" Version"6.0.25" /><PackageReference Include"Microsoft.IdentityModel.Tokens" Version"7.0.3" /><P…

elasticsearch-py 8.x的一些优势

​ 早在 2022 年 2 月,当 Elasticsearch 8.0 发布时,Python 客户端也发布了 8.0 版本。它是对 7.x 客户端的部分重写,并带有许多不错的功能(如下所述),但也带有弃用警告和重大更改。今天,客户端的 7.17 版本仍然相对流行,每月下载量超过 100 万次,占 8.x 下载量的 ~50…

acwing linux 第七讲 环境变量、管道、常用命令、附录

文章目录 管道 概念 要点 举例 环境变量 查看 修改 常用环境变量 常用命令 系统状况 文件权限 文件检索 查看文件内容 用户相关 其他工具 安装软件 附录 Linux权限 本节课讲解的是管道&#xff0c;环境变量&#xff0c;以及常用命令 管道 概念 管道类似文…

使用通道和模式

通过通道、选择语句和最佳实践掌握 Go 中的并发编程 并发编程是构建高效和响应迅速的软件的强大范例。Go&#xff0c;也被称为 Golang&#xff0c;通过通道提供了一种健壮且优雅的解决方案来进行并发通信。在这篇文章中&#xff0c;我们将探讨通道的概念、它们在并发编程中的作…

数据结构 -- 堆

一.堆的概念 1.1 堆是什么 堆也叫做优先队列&#xff0c;一些按照重要性或优先级来组织的对象称为优先队列。 1.2 为什么需要堆 在现实生活中&#xff0c;存在许多需要从一群人、一些任务或一些对象中找出“下一位最重要”目标的情况。例如&#xff1a;在平时处理事情的时候我…

事件和事件源

事件监听 在JS当中写事件监听是这个函数&#xff0c;写了这个函数&#xff0c;前面是DOM对象&#xff0c;当由DOM树和CSSOM树形成的渲染树也有这个监听&#xff0c;这个函数可以添加到DOM树&#xff0c;最后渲染树也有。渲染树会渲染标签当标签发生该事件就会执行这个函数。这个…

操作系统——进程管理算法和例题

1、概述 1.1 进程调度 当进程的数量往往多于处理机的个数&#xff0c;出现进程争用处理机的现象&#xff0c;处理机调度是对处理机进行分配&#xff0c;就是从就绪队列中&#xff0c;按照一定的算法&#xff08;公平、髙效&#xff09;选择一个进程并将处理机分配给它运行&am…

Python---搭建Python自带静态Web服务器

1. 静态Web服务器是什么&#xff1f; 可以为发出请求的浏览器提供静态文档的程序。 平时我们浏览百度新闻数据的时候&#xff0c;每天的新闻数据都会发生变化&#xff0c;那访问的这个页面就是动态的&#xff0c;而我们开发的是静态的&#xff0c;页面的数据不会发生变化。 …

帆软报表 - 数据显示为列表,但是数据仍全部显示在同一行上?

文章目录 1 问题截图2 解决办法3 原因分析3.1 数据设置&#xff1a;全是列表 1 问题截图 想要的效果&#xff1a;每行显示一组数据得到的效果&#xff1a;数据全部显示在一行&#xff0c;以逗号隔开 2 解决办法 修改扩展方向。将 “不扩展” 修改为 “纵向” 3 原因分析 3.1…

消除蛋蛋派

欢迎来到程序小院 消除蛋蛋派 玩法&#xff1a;消除游戏&#xff0c;三个相同形状的蛋蛋连成一条直线即可消除&#xff0c;点击鼠标左键移动球球进行消除&#xff0c; 可以使用道具&#xff0c;共有50关卡&#xff0c;快去闯关吧^^。开始游戏https://www.ormcc.com/play/gameS…

3. BlazorSignalRApp 结合使用 ASP.NET Core SignalR 和 Blazor

参考&#xff1a;https://learn.microsoft.com/zh-cn/aspnet/core/blazor/tutorials/signalr-blazor?viewaspnetcore-8.0&tabsvisual-studio 1.创建新项目 BlazorSignalRApp 2.添加项目依赖项 依赖项&#xff1a;Microsoft.AspNetCore.SignalR.Client 方式1 管理解决方案…

c语言:文件操作(2),认识各种文件操作函数

fgets 作用 fgets是C语言标准库中用于从文件中读取字符串的函数。 fgets函数从指定的文件流stream中读取最多n-1个字符&#xff0c;或者直到遇到换行符&#xff08;包括换行符在内&#xff09;&#xff0c;并将其存储到以str指向的字符数组中。读取的字符串会以null字符\0结…

模拟电路基础知识经典 200问,收藏这些就够了!

大家总说模电知识总是学不会&#xff0c;IC修真院为大家整理了模电经典200问&#xff0c;看看你掌握了多少&#xff0c;文末可以获取全部哦。 文末可领全部文档 1、半导体材料制作电子器件与传统的真空电子器件相比有什么特点? 答&#xff1a;频率特性好、体积小、功耗小&…

信息收集 - 谷歌hack

搜索引擎 FOFA网络空间测绘:https://fofa.info/ FOFA(FOcus on Assets)是一个网络空间搜索引擎,可以帮助用户快速定位和收集特定目标的信息。 ZoomEye:https://www.zoomeye.org ZoomEye 是一个网络空间搜索引擎,可以用于发现和收集特定目标的网络设备、Web应用程序、开放…

OpenCV-Python(18):图像梯度

目录 背景介绍及应用 学习目标 原理 Sobel算子和Scharr算子 Laplacian 算子 代码示例 重要提醒 背景介绍及应用 图像的梯度是指图像中每个像素点的强度变化情况。计算图像的梯度可以帮助我们了解图像中物体的边界和纹理等信息。梯度在计算机视觉和图像处理领域有着广泛…

【Amazon 实验③】Amazon WAF功能增强之追踪 Amazon WAF RequestID,排查误杀原因

文章目录 1. 方案介绍2. 架构图3. 操作演示 本实验将介绍如何利用 Amazon LambdaEdge&#xff0c;在 Amazon CloudFront 自定义错误页面 上展示每个由 Amazon WAF 返回的“403 Forbidden”错误的 Request ID。通过这个唯一的 WAF Request ID&#xff0c;网站运维工程师能够快速…

Swift 周报 第四十一期

文章目录 前言新闻和社区2024 年 Swift Student Challenge 公布现推出超过 30 个新的开发者活动 提案正在审查的提案 Swift论坛话题讨论推荐博文关于我们 前言 本期是 Swift 编辑组整理周报的第四十一期&#xff0c;每个模块已初步成型。各位读者如果有好的提议&#xff0c;欢…

不受父容器大小约束的TextView

序言 为了实现以下效果&#xff0c;特意开发了一个自定义控件。主要是红色的点赞数和评论数。 问题分析 自定义控件 该控件主要是在于忽略的父容器的大小限制&#xff0c;这样可以展示出全部内容。注意父容器的属性中需要下列配置。 package com.trs.myrb.view.count;impor…

[JS设计模式]Flyweight Pattern

Flyweight pattern 享元模式是一种结构化的设计模式&#xff0c;主要用于产生大量类似对象而内存又有限的场景。享元模式能节省内存。 假设一个国际化特大城市SZ&#xff1b;它有5个区&#xff0c;分别为nanshan、futian、luohu、baoan、longgang&#xff1b;每个区都有多个图…

Java 基础学习(十三)集合框架、List集合

1 集合框架 1.1 Collection 1.1.1 集合框架概述 Java 集合框架是一组实现了常见数据结构&#xff08;如列表、树集和哈希表等&#xff09;的类和接口&#xff0c;用于存储一组数据。 开发者在使用Java的集合类时&#xff0c;不必考虑数据结构和算法的具体实现细节&#xff…