设计模式之责任链讲解

责任链模式适用于需要将请求和处理解耦的场景,同时又需要动态地组织处理逻辑的场景。
通过使用责任链模式,可以实现请求的动态处理、灵活的扩展和简化的代码编写,提高系统的可维护性和可扩展性。

一、责任链入门

以下这是GPT生成的责任链代码:

1.定义抽象处理器接口

// 定义抽象处理者接口
interface Handler {void handleRequest(int request);void setNextHandler(Handler handler);
}

2. 具体处理者类1

class ConcreteHandler1 implements Handler {// 定义第二个处理器private Handler nextHandler;@Overridepublic void handleRequest(int request) {if (request >= 0 && request < 10) {System.out.println("ConcreteHandler1 处理请求:" + request);} else if (nextHandler != null) {nextHandler.handleRequest(request);}}@Overridepublic void setNextHandler(Handler handler) {this.nextHandler = handler;}
}

3. 具体处理者类2

class ConcreteHandler2 implements Handler {private Handler nextHandler;@Overridepublic void handleRequest(int request) {if (request >= 10 && request < 20) {System.out.println("ConcreteHandler2 处理请求:" + request);} else if (nextHandler != null) {nextHandler.handleRequest(request);}}@Overridepublic void setNextHandler(Handler handler) {this.nextHandler = handler;}
}

4. 客户端类

public class Client {public static void main(String[] args) {// 创建具体处理者对象Handler handler1 = new ConcreteHandler1();Handler handler2 = new ConcreteHandler2();// 设置处理者之间的关系handler1.setNextHandler(handler2);// 创建请求并发送给责任链的第一个处理者handler1.handleRequest(5);handler1.handleRequest(15);handler1.handleRequest(25);}
}

在这个示例中,Handler 是抽象处理者接口,定义了处理请求的方法 handleRequest 和设置下一个处理者的方法 setNextHandler。ConcreteHandler1 和 ConcreteHandler2 是具体处理者类,分别处理不同范围的请求。Client 是客户端类,创建具体处理者对象并设置它们之间的关系,然后发送请求给责任链的第一个处理者。当请求发送到责任链后,责任链中的处理者按顺序尝试处理请求,直到有一个处理者处理了请求为止。
handler1.handleRequest(15); 表示将请求值为15的请求发送给责任链的第一个处理者 handler1。在当前设计的责任链模式中,一旦有一个处理者处理了请求,处理链就会终止,不再将请求传递给下一个处理者。因此,每次发送请求后,只有第一个能够处理该请求的处理者会对其进行处理。因为具体的实现类中是通过if...else进行处理器的整合的,当然也可以修改为只要满足特定的处理条件,处理器就会继续往下走。

责任链作为一种设计模式,目的是为了让代码更加优雅,复用性更高。

二、上下文责任链

1. 定义订单处理器模板接口

public interface OrderProcessor {// 自定义void processOrder(Order order, OrderContext context);// 公共方法void preRequestHandler(Order order);
}

2. 定义订单处理器抽象类模板

实现公共方法

public abstract class AbstractOrderProcessor implements OrderProcessor {@Overridepublic void preRequestHandler(Order order) {System.out.println("采购员对订单统一处理");}
}

3. 订单处理器实现

3.1 开始处理器
@Component
@HandlerOrder(order = 1)
public class PendingOrderProcessor extends AbstractOrderProcessor {@Overridepublic void processOrder(Order order, OrderContext context) {// 处理待处理的订单逻辑System.out.println("开始订单处理: " + order.getId());// 可以通过上下文对象访问共享信息System.out.println("Context: " + context.getInfo());}
}
3.2 批准处理器
@Component
@HandlerOrder(order = 2)
public class ApprovedOrderProcessor extends AbstractOrderProcessor{@Overridepublic void processOrder(Order order, OrderContext context) {// 处理已批准的订单逻辑System.out.println("批准订单: " + order.getId());// 可以通过上下文对象访问共享信息System.out.println("Context: " + context.getInfo());}
}
3.3 发货处理器
@Component
@HandlerOrder(order = 3)
public class ShippedOrderProcessor extends AbstractOrderProcessor{@Overridepublic void processOrder(Order order, OrderContext context) {// 处理已发货的订单逻辑System.out.println("订单发货: " + order.getId());// 可以通过上下文对象访问共享信息System.out.println("Context: " + context.getInfo());}
}

4. 订单责任链

@Component
public class OrderProcessorChain {private final ApplicationContext applicationContext;Collection<OrderProcessor> processors = new ArrayList<>();// 注入 ApplicationContextpublic OrderProcessorChain(ApplicationContext applicationContext) {this.applicationContext = applicationContext;// 获取所有订单处理器this.processors = applicationContext.getBeansOfType(OrderProcessor.class).values();}// 根据注解值进行排序protected List<OrderProcessor> getProcessors() {return this.processors.stream().sorted((o1, o2) -> {HandlerOrder order1 = o1.getClass().getAnnotation(HandlerOrder.class);HandlerOrder order2 = o2.getClass().getAnnotation(HandlerOrder.class);if (order1 == null && order2 == null) {return 0; // 如果两者都没有注解,则认为它们相等} else if (order1 == null) {return 1; // 如果 o1 没有注解但 o2 有注解,则 o1 排在 o2 前面} else if (order2 == null) {return -1; // 如果 o1 有注解但 o2 没有注解,则 o1 排在 o2 后面} else {return Integer.compare(order1.order(), order2.order()); // 比较两个注解的 order 属性}}).collect(Collectors.toList());}public void process(Order order, OrderContext context) {List<OrderProcessor> processors = getProcessors();// 依次调用订单处理器处理订单for (OrderProcessor processor : processors) {processor.preRequestHandler(order);processor.processOrder(order, context);}}
}

5. 实体类和注解

@Data
public class Order { private int id;
}@Data
public class OrderContext {private String info; // 上下文信息
}// 指定注解的保留策略
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlerOrder {int order();
}

6. 测试

@SpringBootTest
public class OrderProcessorChainTest {@Autowiredprivate OrderProcessorChain processorChain;@Testpublic void testOrderProcessing() {// 创建订单Order order = new Order();order.setId(123);// 创建订单上下文对象OrderContext context = new OrderContext();context.setInfo("Some info for processing orders");// 处理订单processorChain.process(order, context);}
}

结果:

采购员对订单统一处理
开始订单处理: 123
Context: Some info for processing orders
采购员对订单统一处理
批准订单: 123
Context: Some info for processing orders
采购员对订单统一处理
订单发货: 123
Context: Some info for processing orders

三、Spring过滤器链

责任链在Spring中怎么运用呢?
在Spring框架中,责任链模式常常运用在拦截器(Interceptor)和过滤器(Filter)等场景中,用于处理HTTP请求、消息传递等。

拦截器(Interceptor):在Spring MVC中,拦截器用于在处理请求之前或之后执行一些操作,比如权限检查、日志记录等。你可以定义多个拦截器,它们按照顺序构成一个责任链。当一个请求到达时,会依次执行每个拦截器的预处理方法和后处理方法,类似于责任链模式的行为。

过滤器(Filter):在Spring框架中,你也可以使用Servlet过滤器来对HTTP请求进行预处理或后处理。过滤器链在Servlet容器中会按照配置顺序依次执行,也可以看作是一个责任链模式的应用。

事件监听器(Event Listener):Spring框架提供了事件监听器机制,你可以定义自己的事件和监听器,并通过Spring容器进行管理。当某个事件发生时,监听器会按照注册顺序依次被调用,类似于责任链模式。

下面是一个简单的Spring拦截器示例:

1. 定义拦截器

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 在请求处理之前执行的操作,比如权限检查,未登录则直接拦截if (!checkAuth(request)) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return false;}// 放行return true;}// 可以不重写该方法@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 在请求处理之后执行的操作}// 可以不重写该方法@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 在请求完成之后执行的操作}private boolean checkAuth(HttpServletRequest request) {// 检查用户是否有权限// 这里假设简单地检查请求中是否包含合法的身份验证信息String authToken = request.getHeader("Authorization");return authToken != null && authToken.equals("valid_token");}
}

在这个示例中, LoginInterceptor 类实现了 HandlerInterceptor 接口,它是Spring MVC中的拦截器。preHandle 方法用于在请求处理之前执行操作,比如权限检查;postHandle 和 afterCompletion 方法分别用于在请求处理之后执行操作。多个拦截器可以组成一个责任链,按照它们的注册顺序依次执行。
代码中只是提供了拦截器的实现,但是并没有形成责任链,那么多个拦截器是如何形成链路并进行请求的处理的呢?

2. 过滤器链配置

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate EmployeeService employeeService;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns("/user/login","/employee/login").order(2);// 管理员拦截器,用户可操作的功能都排除registry.addInterceptor(new AdminLoginInterceptor(employeeService)).excludePathPatterns("/user/login","/user/logout").order(3);// token刷新的拦截器,order值越小,越先执行,这个拦截器对所有请求都执行registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(1);}}

在上面的代码中MvcConfig 实现了 WebMvcConfigurer 类,并且通过addInterceptors方法项过滤器链中增加了两个拦截器LoginInterceptorAdminLoginInterceptor。这两个拦截器通过order指定了拦截器的顺序。两个拦截器在请求过来时都会触发,拦截器会根据自身配置的拦截路径做过滤处理。
我们可以来看下 WebMvcConfigurer接口的定义:接口定义了很多方法,其中有一个addInterceptors方法就是将拦截器添加到链路中的。这个接口其实有点像我们在责任链入门中讲到的定义抽象处理器接口中的setNextHandler,通过registry.addInterceptor()来将处理器进行组织。

public interface WebMvcConfigurer {... default void addInterceptors(InterceptorRegistry registry) {}
}

InterceptorRegistry类中会通过addInterceptor方法将处理器添加到集合中去。

public class InterceptorRegistry {private final List<InterceptorRegistration> registrations = new ArrayList();private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR;public InterceptorRegistry() {}public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {InterceptorRegistration registration = new InterceptorRegistration(interceptor);this.registrations.add(registration);return registration;}
}

通过addInterceptors方法将拦截器添加到Spring MVC的拦截器链中。当Spring MVC接收到一个请求时,它会通过DispatcherServlet进行处理。DispatcherServlet会调用拦截器链中的每个拦截器来处理请求。

具体来说,以下是请求如何经过拦截器链被拦截器处理的过程:

当一个请求到达DispatcherServlet时,DispatcherServlet会创建一个用于处理该请求的HandlerExecutionChain。

在创建HandlerExecutionChain的过程中,DispatcherServlet会检查是否存在拦截器链。如果存在,DispatcherServlet会将该请求与拦截器链关联起来。

在处理请求之前,DispatcherServlet会依次调用拦截器链中每个拦截器的preHandle方法。拦截器的preHandle方法可以用来进行预处理,比如身份验证、日志记录等操作。

如果拦截器链中的所有拦截器的preHandle方法都返回true,表示请求可以继续处理。此时,DispatcherServlet会调用与请求匹配的处理器(Controller)来处理请求。

处理器处理完请求后,DispatcherServlet会依次调用拦截器链中每个拦截器的postHandle方法。拦截器的postHandle方法可以用来进行后处理,比如修改响应内容等操作。

最后,DispatcherServlet会依次调用拦截器链中每个拦截器的afterCompletion方法。拦截器的afterCompletion方法会在请求完成后被调用,无论请求是否成功处理。

通过这个过程,拦截器能够在请求处理的不同阶段进行干预,实现诸如权限验证、日志记录、异常处理等功能。拦截器链的顺序由拦截器的order方法决定,数值越小的拦截器优先级越高。

四、总结

关于责任链模式,必须要有处理器模板,要有链,链可以是集合,也可以是类似链表。请求就会沿着这条链依次被链上处理器处理。

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

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

相关文章

(三)PostgreSQL的pg_ctl命令

PostgreSQL的pg_ctl命令 pg_ctl 是 PostgreSQL 用于控制数据库服务器进程的命令行工具。它提供了启动、停止、重启数据库服务器以及管理其运行状态的手段。pg_ctl 命令尤其适用于从命令行或脚本中管理 PostgreSQL 服务&#xff0c;而不是通过操作系统的服务控制管理器。 基础…

css 太极图案例带来的收获

基础知识 渐变&#xff1a;gradient 在两个或者多个颜色之间显示平稳过度。由浏览器生成。 线性渐变&#xff1a;line-gradient(过渡方向&#xff0c;初始颜色&#xff0c;结束颜色)。注意过渡方向默认从上到下。 1、支持多颜色渐变&#xff0c;多个值&#xff0c;就是从多个…

Springboot整合物联网IOT的MQTT协议

准备工作 (下载EMQX服务端&#xff0c;相关客户端工具) 1. 服务端工具&#xff1a; https://www.emqx.io/downloads?osWindows 2. 客户端工具&#xff1a; https://mqttx.app/zh#download <!--web依赖--><dependency><groupId>org.springframework.boot…

UnityShader学习计划

1.安装ShaderlabVS,vs的语法提示 2. 常规颜色是fixed 3.FrameDebugger调试查看draw的某一帧的全部信息&#xff0c;能看到变量参数的值

架构设计-权限系统之权限系统设计

系统设计权限系统 权限管控可以通俗的理解为权力限制&#xff0c;即不同的人由于拥有不同权力&#xff0c;他所看到的、能使用的可能不一样。对应到一个应用系统&#xff0c;其实就是一个用户可能拥有不同的数据权限&#xff08;看到的&#xff09;和操作权限&#xff08;使用…

前 5 名 iPhone 数据恢复软件评测

如今&#xff0c;我们似乎将整个生活都放在手机和移动设备上。他们用许多照片、备忘录、日历日期等记录了我们的生活&#xff0c;我们总是假设这些信息在我们需要时随时可以访问。但是&#xff0c;有许多情况会导致iPhone上的数据丢失&#xff0c;例如iPhone被盗&#xff0c;损…

JVM垃圾回收(GC)

目录 目录 1.GC 简介 1.1. 引言 1.2. 何为 GC 1.2.1. 手动 GC 1.2.2. 自动 GC 引用计数法 标记清除 2.GC入门分析 2.1.碎片整理 1)对象创建时&#xff0c;执行写入操作越来越耗时 2&#xff09;内存分配错误 2.2. 分代设想 2.3. 对象分配 对象内存分配过程 2.4. …

数据中心可视化平台:有效提升运维效率与管理水平

随着企业信息化建设的不断深入&#xff0c;数据中心作为支撑企业核心业务的重要基石&#xff0c;其运维管理的复杂性和挑战性也日益凸显。为了提升数据中心机房的管理水平和效率&#xff0c;实现精细化、专业化、规范化和自动化管理&#xff0c;构建数据中心可视化平台成为了一…

【鸿蒙开发】第二十章 Camera相机服务

1 简介 开发者通过调用Camera Kit(相机服务)提供的接口可以开发相机应用&#xff0c;应用通过访问和操作相机硬件&#xff0c;实现基础操作&#xff0c;如预览、拍照和录像&#xff1b;还可以通过接口组合完成更多操作&#xff0c;如控制闪光灯和曝光时间、对焦或调焦等。 2 …

RHCSA 模拟题(4)

请查阅1&#xff1a;RHCSA 模拟题-CSDN博客 请查阅2&#xff1a;RHCSA 模拟题&#xff08;2&#xff09;-CSDN博客 请查阅3&#xff1a;RHCSA 模拟题&#xff08;3&#xff09;-CSDN博客 在node2.example.com上执行以下任务 一、设置root密码 1、重启系统 2、将光标移动到…

《青少年成长管理2024》050 “成长目标:寻找世界的入口”1/2

《青少年成长管理2024》050 “成长目标&#xff1a;寻找世界的入口”1/2 一、蛋壳理论二、正向认知三、逆向认知。四、双向认知。 本节摘要&#xff1a;青少年在建立成长目标之前&#xff0c;需要具备一定的前提&#xff0c;就像寻找一个宝藏&#xff0c;要先找到一个入口&…

dijkstra + dp,PTA 天梯赛练习集L2-001 紧急救援

一、题目 1、题目描述 作为一个城市的应急救援队伍的负责人&#xff0c;你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候&#xf…

5.8 mybatis之EnumTypeHandler详细使用

文章目录 1. 把java中枚举数据插入到数据库中2. 把数据库中值查询到java对象中 在 Java 中&#xff0c;枚举类型是一种特殊的类&#xff0c;当我们在数据库和 Java 对象之间进行映射时&#xff0c;通常需要将数据库中的某个字段&#xff08;如字符串或数字&#xff09;映射到 J…

7天八股速记之C++后端——Day 4

坚持7天&#xff0c;短期内快速完成C后端面试突击。每天10题&#xff0c;弥补后端八股知识缺漏&#xff0c;熟练掌握后端的高频考点&#xff0c;后端面试更有把握。 1. 一条 SQL 语句在数据库框架中的执行流程&#xff1f; 连接数据库&#xff1a; 客户端应用程序通过数据库连…

Github远程仓库改名字之后,本地git如何配置?

文章目录 缘由解决方案 缘由 今天在github创建一个仓库&#xff0c;备份一下本地电脑上的资料。起初随便起一个仓库名字&#xff0c;后来修改之。既然远程仓库改名&#xff0c;那么本地仓库需要更新地址。这里采用SSH格式。 解决方案 如果你的GitHub仓库改名了&#xff0c;你…

Python基于大数据的微博的舆论情感分析,微博评论情感分析可视化系统,附源码

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

练习题(2024/4/13)

1长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0 。 示例 1&am…

2024.4.14每日一题

LeetCode 设计哈希集合 题目链接&#xff1a;705. 设计哈希集合 - 力扣&#xff08;LeetCode&#xff09; 题目描述 不使用任何内建的哈希表库设计一个哈希集合&#xff08;HashSet&#xff09;。 实现 MyHashSet 类&#xff1a; void add(key) 向哈希集合中插入值 key 。…

1. 软件是如何访问硬件的

1 软件是如何访问硬件的 操作系统作为硬件层的上层&#xff0c;是对硬件的管理和抽象。对于操作系统上面的运行库和应用程序来说&#xff0c;他们希望看到的是一个统一的硬件访问模式。作为应用程序开发者&#xff0c;不希望在开发应用程序的时候直接读写硬件端口、处理硬件中断…

Linux第89步_了解异步通知及其结构和函数

1、了解“异步通知” “异步通知”的核心就是信号。信号是采用软件模拟的“中断”&#xff0c;它由“驱动程序”主动向“应用程序”发送信号&#xff0c;并报告自己可以访问了&#xff0c;“应用程序”收到信号以后&#xff0c;就从“驱动设备”中读取或者写入数据。整个过程就…