聊聊如何实现一个特别的责任链

news/2024/10/15 9:28:25

前言

什么是责任链

责任链是一种设计模式,它让多个对象有机会处理同一个请求,这些对象形成一个链。请求从链的一端开始,逐个传递给链上的对象,直到某个对象处理它或者请求未被处理。这样,发送请求者无需知道哪个对象会处理,实现了发送者与接收者的解耦,增加了系统的灵活性

责任链的常用场景

  • 权限与认证系统:在登录认证、权限验证流程中,不同的处理者可以检查用户名密码、验证权限级别、处理单点登录等。请求(如访问资源)沿着责任链传递,直到找到合适的处理器来授权或拒绝访问。
  • 日志记录与错误处理:根据日志级别(如DEBUG, INFO, WARNING,
    ERROR)或错误类型,不同的处理器负责记录或处理相应的日志信息或异常。开发者可以灵活地插入或移除处理逻辑,而不影响其他日志处理。
  • 请求过滤与处理:在Web服务或API网关中,责任链可用于实现一系列预处理任务,如参数校验、请求限流、IP黑名单过滤、会话管理等,每个处理环节关注于特定的验证或转换逻辑。
  • 命令与事件处理:在处理一系列可选的或有顺序依赖的命令或事件时,责任链允许按照预定的逻辑顺序尝试执行处理逻辑,直到命令被执行或事件被妥善处理。
  • 工作流与审批流程:在企业应用中,审批流程可以设计成责任链,每个节点代表一个审批层级或角色,请求(如报销单)依次经过各个审批节点,直至最终批准或拒绝。
  • UI事件处理:在图形界面应用程序中,事件(如鼠标点击、键盘输入)可以通过责任链分发给不同的组件,每个组件决定是否消费此事件,未处理的事件继续传递给链中的下一个组件。

今天给大家的介绍的责任链有点特殊,它是基于Pipeline-Valve模型,这种模型跟常规的责任链模式有点区别

  • 每个Pipeline都是有特定的Valve,而且是在管道的最后一个执行,这个Valve叫BaseValve,并且BaseValve是不可删除的;
  • 在上层容器的管道的BaseValve中会调用下层容器的管道。

示例图:

如何实现Pipeline-Valve模型

1、定义Valve接口

该接口表示处理链中的单个处理单元。

public interface Valve extends Ordered {Valve getNextValve();void setNextValve(Valve next);void invoke(ValveContext context);default boolean isBaiscValve() {return false;}}

2、定义抽象valve(可选)

注:定义该抽象valve主要是为了复用

public abstract class AbstractValve implements Valve {protected Valve nextValve;@Overridepublic Valve getNextValve() {return nextValve;}@Overridepublic void setNextValve(Valve next) {this.nextValve = next;}@Overridepublic void invoke(ValveContext context) {doInvoke(context);if(nextValve!=null){nextValve.invoke(context);}}public abstract void doInvoke(ValveContext context);}

3、 定义Pipeline接口

该接口主要用来用于管理Valve的集合,并提供方法来添加Valve、设置特定valve以及启动处理流程。

public interface Pipeline {void setBasic(Valve valve);void addValve(Valve valve);void process(ValveContext context);
}

4、定义Pipeline的默认实现

public class StandardPipeline implements Pipeline {/*** 第一个阀门*/protected Valve first;/*** 最后一个阀门*/protected Valve basic;@Overridepublic void setBasic(Valve valve) {validateValve(valve,true);this.basic = valve;}@Overridepublic void addValve(Valve valve) {validateValve(valve,false);if(first == null){this.first = valve;valve.setNextValve(basic);}else{Valve current = first;while(current != null){if(current.getNextValve() == basic){current.setNextValve(valve);valve.setNextValve(basic);break;}current = current.getNextValve();}}}@Overridepublic void process(ValveContext context) {if(first != null){if(context == null){context = new ValveContext();}first.invoke(context);}}public void validateValve(Valve valve,boolean isCheckBasicValve){Assert.notNull(valve, "valve must not be null");if(isCheckBasicValve){Assert.isTrue(valve.isBaiscValve(), "valve must be basic valve");}}
}

注: addValue else流程,是为了保证basic阀门一定是在流程最后被调用

5、定义具体valve

@Component
public class FirstValve extends AbstractValve {@Overridepublic void doInvoke(ValveContext context) {String requestId = "lybgeek-" + UUID.randomUUID().toString();System.out.println("第一道阀门: requestId-->【" + requestId + "】");Map<String,Object> request = context.getRequest();request.put("source",FirstValve.class.getSimpleName());context.setRequest(request);context.setRequestId(requestId);}@Overridepublic int getOrder() {return 1;}
}

其他阀门类似,就不列举了

6、通过Pipeline 驱动valve

public class PipelineMainTest {public static void main(String[] args) {Pipeline pipeline = new StandardPipeline();pipeline.setBasic(new BasicValve());pipeline.addValve(new FirstValve());pipeline.addValve(new SecondValve());pipeline.addValve(new ThirdValve());pipeline.process(new ValveContext());}
}

总结

如果大家对tomcat有了解的话,就会知道本文的实现其实就是tomcat的pipeline-valve的简化版实现。其次上文pipeline驱动valve的步骤可以托管给spring,文末demo链接的代码,有做了相应实现,感兴趣的朋友,可以点击文末链接查看

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-pipeline-valve

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

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

相关文章

修改网站源代码的步骤

修改网站源代码通常涉及以下几个步骤:备份现有代码:在进行任何更改之前,确保对当前的源代码进行完整备份。这有助于在出现问题时恢复到修改前的状态。获取源代码:通过FTP/SFTP、Git或其他版本控制系统将网站源代码下载到本地计算机上。分析需求:明确你需要做出哪些具体修改…

宝塔面板添加网站

宝塔面板是一款非常流行的服务器管理软件,它可以帮助用户方便地管理服务器上的网站、数据库、域名等资源。要在宝塔面板上添加一个新的网站,你可以按照以下步骤操作:登录宝塔面板:打开浏览器,输入宝塔面板的IP地址加上端口号(默认为8888)。 输入用户名和密码登录。进入网…

SQLSTATE[HY000] [1045]Access denied for user

遇到 SQLSTATE[HY000] [1045] Access denied for user 这个错误通常表示MySQL拒绝了用户的访问请求,通常是由于用户名或密码不正确导致的。以下是一些解决此问题的步骤:检查用户名和密码确认你使用的用户名和密码是否正确。 尝试在MySQL命令行工具中直接登录以验证。重置密码…

请问公司网站改版方案有哪些?企业网站改版需要注意什么

用户体验优化简化导航结构,使用户更容易找到所需信息。 提升页面加载速度,减少用户等待时间。 优化移动设备访问体验,确保网站在手机和平板上也能良好展示。视觉设计更新采用现代设计风格,提升品牌形象。 调整色彩搭配和字体样式,增强视觉吸引力。 引入高质量图片和视频,…

一款由AI编写,简洁而实用的开源IP信息查看器

大家好,今天给大家分享一款用于查询和显示用户当前 IP 地址的轻量级项目MyIP。MyIP提供了多种功能,包括IP地址查询、网络连通性检查、WebRTC连接检测、DNS泄露检查、网速测试、MTR测试等等。 使用MyIP,我们可以轻松地查看自己的公网IP地址,并且可以方便地进行网络诊断或监控…

公司网址怎么制作

制作公司网站通常涉及以下几个步骤:确定需求: 首先明确你的网站目标是什么,比如展示公司信息、产品或服务介绍、招聘信息等。 域名注册: 选择一个与公司名称或业务相关的域名,并从域名提供商处注册。 选择主机: 根据网站规模和流量预期选择合适的服务器或虚拟主机服务。 设计…

网站为什么要定期修改后台和FTP密码?

定期修改后台和FTP密码是保障网站安全的重要措施之一,主要原因包括:降低风险:即使密码已被泄露或存在潜在的安全漏洞,定期更改密码可以减少这些信息被滥用的时间窗口。 增强安全性:频繁更换密码能够提高破解难度,使得攻击者更难以通过暴力破解或其他手段获取访问权限。 符…

公司网站打开是乱码怎么办?

公司网站出现乱码问题通常与字符编码设置不正确有关,可以按照以下步骤来排查和解决:检查HTML头部编码声明确认<head>标签内是否有正确的<meta>标签指定字符集,例如:<meta charset="UTF-8">服务器端编码设置检查服务器端是否正确设置了响应头中…