wrapper怎么用_用责任链模式设计拦截器

我在 Redant(https://github.com/all4you/redant) 中通过继承 ChannelHandler 实现了拦截器的功能,并且 pipeline 就是一种责任链模式的应用。但是我后面对原本的拦截器进行了重新设计,为什么这样做呢,因为原本的方式是在 ChannelHandler 的基础上操作的,而我们知道 Netty 的数据处理都是基于 ByteBuf 的,这就涉及到引用计数释放的问题,前面的 ChannelHandler 在处理时可以不关心引用计数的问题,而交给最后一个 ChannelHandler 去释放。

但是拦截器的一大特性就是当某个条件不满足时需要中断后面的操作直接返回,所以这就造成了在 pipeline 中某个节点需要释放引用计数,另外一个方面就是原先的设计使用了很多自定义的 ChannelHandler,有的只做了一些简单的工作,所以完全可以对他们进行合并,使代码变得更加精简紧凑。

合并多个 ChannelHandler 是比较简单的,重新设计拦截器相对就复杂一些了。

重新设计拦截器

首先我把原本的前置拦截器和后置拦截器统一成一个拦截器,然后抽象出两个方法,分别表示:前置处理,后置处理,如下图所示:

d2eb743005e99e11c94e4ea4d5578eee.png

默认前置处理的方法返回 true,用户可以根据他们的业务进行覆盖。

这里是定义了一个抽象类,也可以用接口,java 8 开始接口中可以有默认方法实现。

拦截器定义好之后,现在就可以在 ChannelHandler 中加入拦截器的方法调用了,如下图所示:

a30fd978af459310a24643a88acc0bf4.png

当前置方法返回 false 时,直接返回,中断后面的业务逻辑处理,最终会到 finally 中将结果写入 response 中返回给前端。

现在只要实现 InterceptorHandler 中的两个方法就可以了,其实这也很简单,只要获取到所有的 Interceptor 的实现类,然后依次调用这些实现类的前置方法和后置方法就好了,如下图所示:

d86e60147edc41905f67c7362ee818e9.png

获取拦截器

现在的重点就是怎样获取到所有的拦截器,首先可以想到的是通过扫描的方法,找到所有 Interceptor 的实现类,然后将这些实现类加入到一个 List 中即可。

那怎么保证拦截器的执行顺序呢,很简单,只要在加入 List 之前对他们进行排序就可以了。再定义一个 @Order 注解来表示排序的顺序,然后用一个 Wrapper 包装类将 Interceptor 和 Order 包装起来,排序到包装类的 List 中,最后再从包装类的 List 中依次取出所有的 Interceptor 就完成了 Interceptor 的排序了。

知道了大致的原理之后,实现起来就很简单了,如下图所示:

5e9243f2d0af31995c8cd09c006f7c3d.png

但是我们不能每次都通过调用 scanInterceptors() 方法来获取所有的拦截器,如果这样每次都扫描一次的话性能会有影响,所以我们只需要第一次调用一下该方法,然后把结果保存在一个私有的变量中,获取的时候直接读取该变量的值即可,如下图所示:

4ef09a5c294e04ea085669bee2c8f8f6.png

自定义拦截器实现类

下面让我们来自定义两个拦截器实现类,来验证下具体的效果。

第一个拦截器,在前置方法中对请求参数进行判断,如果请求参数中有 block=true 的参数,则进行拦截,如下图所示:

c5f5ffc83dfaf03c5b5c47d8bbc4d481.png

第二个拦截器,在后置方法中打印出每次请求的耗时,如下图所示:

91173e6d9fb3573780fdcfae079cfcdd.png

通过 @Order 注解来指定执行的顺序,先执行 BlockInterceptor 再执行 PerformanceInterceptor。

查看效果

现在我们请求 /user/info 这个接口,查看下效果。

首先我们只提交正常的参数,如下图所示:

4c48a9326ecbf3e7aa8db9ea8ff53463.png

打印的结果如下图所示:

de3776cc7a5ee0fbb9788ce23fed716a.png

从打印的结果中可以看到依次执行了:

  • BlockInterceptor 的 preHandle 方法

  • PerformanceInterceptor 的 preHandle方法

  • BlockInterceptor 的 postHandle 方法

  • PerformanceInterceptor 的 postHandle方法

这说明拦截器是按照 @Order 注解进行了排序,然后依次执行的。

然后我们再提交一个 block=true 的参数,再次请求该接口,如下图所示:

d9d5af06745b2e0b6002a8d8a9ff4a57.png

可以看到该请求已经被拦截器的前置方法给拦截了,再看下打印的日志,如下图所示:

f59e776d0e494c3c1d865710afb2e257.png

只打印了 BlockInterceptor 的 preHandler 方法中的部分日志,后面的方法都没有执行,因为被拦截了直接返回了。

存在的问题

到这里已经对拦截器完成了改造,并且也验证了效果,看上去效果还可以。但是有没有什么问题呢?

还真有一个问题:所有的 Interceptor 实现类只要被扫描到了,就会被加入到 List 中去,如果不想应用某一个拦截器这时就做不到了,因为无法对 list 中的值进行动态的更改。

如果我们可以动态的获取一个保存了 Interceptor 的 list ,如果该 list 中没有获取到值,再通过扫描的方式去拿到所有的 Interceptor 这样就完美了。

动态获取 Interceptor 的 list 的方法,可以由用户自定义实现,根据某些规则来确定要不要将某个 Interceptor 加入到 list 中去,这样就把 Interceptor 的实现和使用进行了解耦了。用户可以实现任意多的 Interceptor,但是只根据规则去使用其中的某些 Interceptor。

理清楚了原理之后,就很好实现了,首先定义一个接口,用来构造 Interceptor 的 List,如下图所示:

c5b2bd8a1e44561b0f1eddc772291c36.png

有了 InterceptorBuilder 之后,在获取 Interceptor 的时候,就可以先根据 InterceptorBuilder 来获取了,如下图所示:

3e0603b7b7c33eb69ebad206386a84b0.png

以下是一个示例的 InterceptorBuilder,具体的可以用户自行扩展设计,如下图所示:

ead168d4349a0f16a367ab2f7fbc086e.png

这样用户只要实现一个 InterceptorBuilder 接口,即可按照自己的意图去组装所有的拦截器。

链式责任链

在 Redant 中实现的拦截器所使用的责任链,其实是通过一个 List 来保存了所有的 Interceptor,那我们通常所说的责任链除了使用 List 来实现外,还可以通过真正的链表结构来实现,Netty 和 Sentinel 中都有这样的实现,下面我来实现一个简单的链式结构的责任链。

责任链的应用已经有很多了,这里不再赘述,假设我们需要对前端提交的请求做以下操作:鉴权,登录,日志记录,通过责任链来做这些处理是非常合适的。

首先定义一个处理接口,如下图所示:

e8e8c426e019b658a328858b38672184.png

通过 List 方式的实现很简单,只需要把每个 Processor 的实现类添加到一个 List 中即可,处理的时候遍历该 List 依次处理,这里不再做具体的描述,感兴趣的可以自行实现。

定义节点

如果是通过链表的形式来实现的话,首先我们需要有一个类表示链表中的某个节点,并且该节点需要有一个同类型的私有变量表示该节点的下个节点,这样就可以实现一个链表了,如下图所示:

fa882155970a768df28cd136c11054f8.png

定义容器

接着我们需要定义一个容器,在容器中有头,尾两个节点,头结点作为一个空节点,真正的节点将添加到头结点的 next 节点上去,尾节点作为一个指针,用来指向当前添加的节点,下一次添加新节点时,将从尾节点处添加。有了具体的处理逻辑之后,实现起来就很简单了,这个容器的实现如下图所示:

1de5b87da12ae35d4e912bbdea815e15.png

定义实现类

下面我们可以实现具体的 Processor 来处理业务逻辑了,只要继承 AbstractLinkedProcessor 即可,如下图所示:

450c23d6987da693bad27d3abbcd2f53.png

其他两个实现类: LoginProcessor ,LogProcessor 类似,这里就不贴出来了。

然后就可以根据规则来组装所需要的 Processor 了,假设我们的规则是需要对请求依次进行:鉴权,登录,日志记录,那组装的代码如下图所示:

effedd1f7fa9d0ef6136b77aa842bc4c.png

执行该代码,结果如下图所示:

0fbd9b5eeb604c304b0a96737d626349.png

存在的问题

看的仔细的同学可能发现了,在 AuthProcessor 的业务逻辑实现中,除了执行了具体的逻辑代码之外,还调用了一行 super.process(content) 代码,这行代码的作用是调用链表中的下一个节点的 process 方法。但是如果有一天我们在写自己的 Processor 实现类时,忘记调用这行代码的话,会是怎样的结果呢?

结果就是当前节点后面的节点不会被调用,整个链表就像断掉一样,那怎样来避免这种问题的发生呢?其实我们在 AbstractProcessor 中已经实现了 process 方法,该方法就是调用下个节点的 process 方法的。那我们在这个方法触发调用下个节点之前,再抽象出一个用以具体的业务逻辑处理的方法 doProcess ,先执行 doProcess 方法,执行完之后再触发下个节点的 process ,这样就不会出现链表断掉的情况了,具体的实现如下图所示:

07d007856ede535a9e389e2e8d7a6af7.png

相应的 LinkedProcessorChain 和具体的实现类也要做响应的调整,如下图所示:

3d2d5b569705f243e2dacec1ad3c6d1a.png

afb8e61c08177bea55c03dfc58396073.png

重新执行刚刚的测试类,发现结果和之前的一样,至此一个简单的链式责任链完成了。

关注「逅弈逐码」

查看更多原创好文

403304f45f12d3499dd8827e1fcb95ee.png

更多推荐内容

↓↓↓

《Sentinel 原理:控制台是如何获取到实时数据的》

《Sentinel 实战:搭建集群限流环境》

《Netty 实战:如何编写一个麻小俱全的 Web 容器》

《让人欢喜让人忧的正则表达式》

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

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

相关文章

VS2019 配色_这个双11,凯里-欧文陪你过!欧文6正式发布,城市版配色真的香

北京时间11月5日,在篮网主场对阵鹈鹕的比赛中,欧文一上场就让教主眼前一亮,这鞋骚的有点过分啊。欧文6代已经于北京时间11月5日正式发布,虽然首发配色毫无惊喜,毕竟早在休赛期就已经被曝得连底裤都不剩了,但…

Linux的实际操作:文件目录类的实用指令(echo head tail)

1.echo 输出内容到控制台 例子:实用echo指令输出环境变量,输出当前的环境变量路径 2.head 用于显示文件的开头部分内容,默认情况下head显示文件的前10行内容(因为有些文件比较大,我们有时候不全去看) 空格…

如何延长作用域链_通过实例理解javaScript中的this到底是什么和它的词法作用域...

最近,听到李笑来说,讲解编程的过程中,举例子很重要。而且,我最近看的各种javaScript工具书中的例子,也都有点复杂。所以啊,我试着举一些简单又直观的例子,与各位苦学javaScript的同学&#xff0…

word2vector数据集样式_这样做数据可视化驾驶舱,高端大气,一目了然,领导不点赞都难...

2020年了,数据可视化已经不是个新鲜词了,把数据以可视化图表的形式展示并没有多神奇,用Excel等传统的办公工具就可以轻松实现。以前传统数据分析报告就是ppt里贴图表,再配上分析结论,这种形式的数据呈现是静态的&#…

Linux的实际操作:文件目录类的实用指令(ln history)

1.ln 软连接也叫做符号链接,类似于windows的快捷方式,主要是存放了链接其它文件的路径 ln -s 原文件或者目录 软链接名 (功能:给原文件创建一个软链接) 案例:将/roo…

mysql 删除重复数据_日常答疑|MySQL删除重复数据踩过得坑

问题群友提问:MySQL这样删除重复数据为啥不成功呢?严小样儿:安排!咋一看,大家都说where子句里面应该使用极值函数,加个max就对了,这么简单!# 大家想象中这样写是对的,其实…

搜狗手机浏览器_搜狗推广开户费多少钱?【搜狗开户代理商】

搜狗开户流程提供落地页审核提供服务器ip 安排解析账户预存款4000币上传关键词及素材开启广告上线福如海广告,搜狗开户代理商全国行业开户,有无资质均可手机:18705657032【同微信】搜狗广告详细介绍搜狗广告展现形式PC端-搜狗搜索推广当网民在…

understand 6.0_软件工程专业篇-vc++6.0安装教程(解决win10不兼容)

在进行安装教程之前还是和大家说几句废话啊。VC6.0就是出品的一款开发C的IDE。所谓IDE就是:为了方便程序员开发,集成了代码编辑器、编译器、链接器、调试器等工具的集成开发环境。 现在外面企业已经很少有使用VC6.0的了,毕竟已经是将近17年前…

Linux的实际操作:时间日期类的实用指令(date cal)

1.date 显示当前日期 (1)date 显示当前时间 (2) date %Y 显示年份 date%m 显示月份 date%d 显示日 date "%Y-%m-%d %H:%M:%S" 显示年月日时分秒 注意:加号不能少 双引号中间的连接可以取任意符号 &a…

Linux的实际操作:搜索查找类的实用指令(find locate grep 管道符|)

1.find 将从指定目录向下递归地遍历其各个子目录,将满足条件的文件或者目录显示在终端 find 【搜索范围】 【选项】 文件 (搜索范围往往是绝对路径或者相对路径) 选项功能-name按照指定文件名查找文件-user查找属于指定用户名所有…

error:java:无效的源发行版_IDEA java出现无效的源发行版14解决方案_java

这篇文章主要介绍了IDEA java出现无效的源发行版14解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下问题描述问题原因出现该问题的原因是项目Project当中的jdk与电脑当中的jdk版本不一致造成的。解决方法…

为什么买入不了创业版_详解实战抄底——如何抄到下引线的最底端。及妖股的首板买入点...

11月26日上周四,如果方便的朋友可以打开下创业板当天的分时图,更加有利于理解。(下图所示)主板和创业板11月26日分时图当天创业板冲高回落开始探底,我在10点17分的时候提示准备抄底,在10点22分的时候,再次强调创业板跌…

Linux的实际操作:文件目录类实用指令(压缩gzip tar -zcvf和解压缩gunzip tar -zxvf)

1.gzip 用于压缩文件 (压缩文件后,原文件就会消失自动成为一个新的压缩文件,意思是压缩后不保留原文件) 2.gunzip 用于解压文件 (解压缩后,压缩文件也会自动生成一个未解压文件&…

keil+proteus 制作计算器_设计费 | 工程设计费计算器使用指南

工程设计费,即工程设计收费,是设计人根据发包人的委托,提供编制建设项目初步设计文件、施工图设计文件、非标准设备设计文件、施工图预算文件、竣工图文件等服务所收取的费用。工程设计收费标准2002年国家发展计划委员会和建设部发布了计价格…

解决:Xshell如何修改默认的登录用户,自动跳转到密码界面

1.打开Xshell会话,选择远程主机号,点击“打开会话文件夹” 2.右键,用记事本打开 3.打开后CtrlF搜索UserName 4.将参数改为空的即可

wegame一键蹲替换文件_如何在ps里批量替换姓名、编号等内容(变量和图像处理器)...

大家好今天的教程比较冷门但是应用的却比较广泛就是变量图像处理器比如会员卡、胸牌等有很多姓名或标号需要替换时这个组合技简直无敌命令位置【图像-变量-定义】【文件-脚本-图像处理器】具体用法以我这张图为例我需要把【号码】图层的内容替换成01-09那么我需要准备些什么呢&…

小米SN保修_首次体验小米手机官方售后全程回顾:维修速度快服务态度好

作为一名搞机玩家,这么多年买了很多也用了很多的小米产品,这是第一次使用小米的官方售后服务,所以趁着这个机会给大家说一下售后的感受:故障机是小米MIX 3已过保,老爸用的屏幕摔坏了一角,时间长了加上又摔所…

Linux的实际操作:用户管理(查ls -ahl,chown改文件所属者,chgrp改文件所属组,usermod改用户所属组)

在Linux中的每个用户必须属于一个组,不能独立于组外 在Linux中每个文件的所属者、所属组、其它组的概念 例如有一个文件是user1创建的,那么这个文件的所属者就是user1,这个文件在默认情况下属于user1所在的组当然,这个所属组也可…

restful接口开发实例_Restful接口开发与测试—接口测试

开发完接口,接下来我们需要对我们开发的接口进行测试。接口测试的方法比较多,使用接口工具或者Python来测试都可以,工具方面比如之前我们学习过的Postman或者Jmeter ,Python脚本测试可以使用Requests unittest来测试。测试思路功…

博主已开启评论精选什么意思_小白必看!想要成为小红书博主,首先要掌握4个工具!...

首先我们要明确一点,不是每个人都要成为小红书博主。但,如果你想要成为博主,那么就需要了解下面这4个工具,可以让你成功迈出小红书博主的第一步。01搜秀数据小红书数据分析平台。四大功能:最新推广情报、KOL智能筛选、…