责任链设计模式(Chain of Responsibility Pattern)[论点:概念、组成角色、图示、相关代码、框架中的运用、适用场景]

news2024/12/23 6:02:31

文章目录

  • 概念
  • 组成角色
  • 相关图示
  • 示例代码
  • 框架中的应用
  • 适用场景:

概念

        责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,其主要目的是将请求的发送者和接收者解耦。这种模式创建了一系列处理器对象,每个处理器都有一个对应的处理逻辑。当一个请求到来时,这些处理器按照顺序处理请求,直到其中一个处理器成功处理请求为止。

组成角色

  1. 抽象处理器(Handler):定义了一个处理请求的接口,包含一个指向下一个处理器的引用。
  2. 具体处理器(ConcreteHandler):实现抽象处理器接口,执行具体的处理逻辑,并决定请求是否传递给下一个处理器。
  3. 客户端(Client):负责创建处理器链,并向处理器链发送请求。

相关图示

在这里插入图片描述

示例代码

//定义抽象处理器(Handler)
abstract class Handler {
    protected Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract void handleRequest(String request);
}



//定义具体处理器(ConcreteHandler) HandlerA、HandlerB、HandlerC
class HandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("A".equals(request)) {
            System.out.println("Handler A is processing the request");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class HandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("B".equals(request)) {
            System.out.println("Handler B is processing the request");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class HandlerC extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("C".equals(request)) {
            System.out.println("Handler C is processing the request");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

//客户端代码
public class Client {
    public static void main(String[] args) {
      	/**客户端创建了三个具体处理器,并将它们连接成一个处理器链。当客户端向处理器链发送请求时,处理器会按照顺序处理请求,直到其中一个处理器成功处理请求为止。**/
        // 创建处理器对象
        Handler handlerA = new HandlerA();
        Handler handlerB = new HandlerB();
        Handler handlerC = new HandlerC();

        // 构建处理器链
        handlerA.setNextHandler(handlerB);
        handlerB.setNextHandler(handlerC);

        // 发送请求
        handlerA.handleRequest("A");
        handlerA.handleRequest("B");
        handlerA.handleRequest("C");
        handlerA.handleRequest("D");
    }
}

框架中的应用

        拦截器链(Interceptor Chain):在Spring MVC框架中,当请求经过过滤器链处理并到达Servlet之后,请求会被传递给DispatcherServlet。DispatcherServlet的主要职责是将请求分发给适当的处理器(如Controller方法)。

Spring MVC中拦截器的执行流程:

  1. 请求到达DispatcherServlet
  2. DispatcherServlet根据请求获取处理器(Handler)和拦截器链(Interceptor Chain)。
  3. DispatcherServlet遍历拦截器链,依次执行拦截器的preHandle方法。如果某个拦截器的preHandle方法返回false,则中止后续拦截器和处理器的执行,并立即返回响应。preHandle方法通常用于执行请求预处理逻辑,例如身份验证和授权检查。
  4. 如果所有拦截器的preHandle方法都返回true,则DispatcherServlet执行处理器(Handler)来处理请求。
  5. 处理器处理完请求后,DispatcherServlet再次遍历拦截器链,按照相反的顺序依次执行拦截器的postHandle方法。postHandle方法在处理器执行之后、视图渲染之前调用,通常用于添加模型属性或对请求进行后处理。
  6. 最后,DispatcherServlet渲染视图并返回响应。
  7. 在请求处理完成并且视图渲染完成之后,DispatcherServlet再次遍历拦截器链,按照相反的顺序依次执行拦截器的afterCompletion方法。afterCompletion方法通常用于清理资源和记录日志等操作。

抽象拦截处理器(Handler)

public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;

    void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}

处理器执行链(HandlerChain)

public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
     // 处理器对象
    private final Object handler;

    // 拦截器数组
    private HandlerInterceptor[] interceptors;

    // 拦截器列表
    private List<HandlerInterceptor> interceptorList;

    // 当前处理的拦截器索引
    private int interceptorIndex;

    public HandlerExecutionChain(Object handler) {
        this(handler, (HandlerInterceptor[]) null);
    }

    public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
        this.interceptorIndex = -1;
        if (handler instanceof HandlerExecutionChain) {
            HandlerExecutionChain originalChain = (HandlerExecutionChain)handler;
            this.handler = originalChain.getHandler();
            this.interceptorList = new ArrayList();
            CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
            CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
        } else {
            this.handler = handler;
            this.interceptors = interceptors;
        }

    }


    public void addInterceptor(HandlerInterceptor interceptor) {
        this.initInterceptorList().add(interceptor);
    }

    public void addInterceptors(HandlerInterceptor... interceptors) {
        if (!ObjectUtils.isEmpty(interceptors)) {
            this.initInterceptorList().addAll(Arrays.asList(interceptors));
        }

    }

    private List<HandlerInterceptor> initInterceptorList() {
        if (this.interceptorList == null) {
            this.interceptorList = new ArrayList();
            if (this.interceptors != null) {
                this.interceptorList.addAll(Arrays.asList(this.interceptors));
            }
        }

        this.interceptors = null;
        return this.interceptorList;
    }

    public HandlerInterceptor[] getInterceptors() {
        if (this.interceptors == null && this.interceptorList != null) {
            this.interceptors = (HandlerInterceptor[])this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
        }

        return this.interceptors;
    }
		
  	// 执行拦截器链中的preHandle方法
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }

  
  	// 执行拦截器链中的postHandle方法
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }

    }

  
  	// 触发拦截器链中的afterCompletion方法
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = this.interceptorIndex; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];

                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } catch (Throwable var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }

}

适用场景:

  1. 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时,可以使用责任链模式。
  2. 当必须按顺序执行多个处理者时,可以使用该模式。
  3. 如果所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式。

以下是一些责任链设计模式的典型应用场景:

  • 日志记录:不同级别的日志消息可以由不同的日志处理器处理,例如,DEBUG、INFO、WARNING和ERROR级别的日志分别由不同的处理器处理。
  • 权限验证:根据用户的权限和角色对请求进行多层次的验证。例如,先验证用户身份,然后验证用户是否有访问特定资源的权限。
  • 请求过滤:在Web应用中,可以使用责任链模式实现请求过滤器,对请求进行预处理(如身份验证、数据验证等)和后处理(如添加响应头、日志记录等)。
  • 事件处理:在图形用户界面(GUI)中,事件可以由多个处理器处理,如按钮点击、键盘输入等。责任链模式可以实现事件处理的优先级和顺序。

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

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

相关文章

Shell Script

目录Shell Script的概述Shell的主要版本Shell脚本的建立与执行Shell中的变量Shell中的特殊字符通配符双引号倒引号&#xff08;&#xff09;顺序分隔符&#xff08;&#xff1b;&#xff09;管道符逻辑与&#xff08;&&&#xff09;和逻辑或&#xff08;||&#xff09;…

synchronized原理详解

众所周知&#xff0c;使用多线程可以极大地提升程序的性能&#xff0c;但如果多线程使用不合理&#xff0c;也会带来很多不可控的问题&#xff0c;例如线程安全问题。 什么是线程安全问题呢&#xff1f;如果多个线程同时访问某个方法时&#xff0c;这个方法无法得到我们预期的…

同步FIFO、异步FIFO详细介绍、verilog代码实现、FIFO最小深度计算、简答题

文章目录前言一、多bit数据流跨时钟域传输——FIFO1、FIFO分类2、常见参数3、与普通存储器的区别4、FIFO优缺点二、同步FIFO1、计数器法2、高位扩展法3、单端口和双端口RAM3.1 单端口RAM3.2 双端口RAM4、例化双端口RAM实现同步FIFO三、异步FIFO1、格雷码1.1 二进制和格雷码之间…

spring5(五):AOP操作

spring5&#xff08;五&#xff09;&#xff1a;AOP操作前言一、代理模式1、场景模拟2、代理模式2.1 概念2.2 静态代理2.3 动态代理二、AOP概述1、什么是 AOP?2、相关术语3、作用三、AOP底层原理1、AOP 底层使用动态代理2、AOP&#xff08;JDK 动态代理&#xff09;2.1 编写 J…

VR全景展示,VR全景平台,助理全景展示新模式

引言&#xff1a; VR全景展示是一种新型的展示方式&#xff0c;它利用虚拟现实技术和全景拍摄技术&#xff0c;使参观者可以身临其境地进入虚拟展览空间。这种展示方式不仅能够提供更加沉浸式的参观体验&#xff0c;还可以解决传统展览所面临的时间和地域限制等问题。 VR全景展…

【Java实战篇】Day7.在线教育网课平台

文章目录一、需求&#xff1a;课程审核1、需求分析2、建表与数据模型3、接口定义4、Mapper层开发5、Service层开发6、完善controller层二、需求&#xff1a;课程发布1、需求分析2、建表与数据模型3、技术方案4、接口定义5、消息处理SDK6、Mapper层开发7、Service层开发8、页面静…

unity,射手游戏

文章目录介绍一&#xff0c;制作玩家具体函数脚本PlayerCharacter三、 制作玩家控制脚本 PlayerController&#xff0c;调用上面的函数方法四、 制作子弹脚本 shell五、 给玩家挂载脚本六、 制作坦克脚本七、 给坦克添加组件八、 开始游戏&#xff0c;播放动画九、 下载介绍 3…

seata学习笔记

Seata 官网&#xff1a; https://seata.io/zh-cn/index.html 是什么&#xff1f; Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式…

MySQL中的float类型慎用!慎用!慎用!

在前端输入一串数字后有时候展示值与输入的内容一致&#xff0c;有时候却不一致。经分析&#xff0c;原来是MySQL数据库中字该字段的类型是float&#xff0c;该字段的值超过6位有效数字后就会进行四舍五入截取&#xff0c;举例来说&#xff1a;假设float类型字段的输入值是1234…

十八、MySQL 变量、分支结构IF、CASE...WHEN详解

文章目录一、变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量1.2 用户变量1.2.1 用户变量分类1.2.2 会话用户变量1.2.3 局部变量1.2.4 对比会话用户变量与局部变量二、定义条件与处理程序2.1 案例分析2.2 定义条件2.3 定义处理程序2.4 案例解决三、流程控制3.1 分支结构…

[C++]类与对象下篇

目录 类与对象下篇&#xff1a;&#xff1a; 1.再谈构造函数 2.static成员 3.友元 4.内部类 5.匿名对象 6.拷贝对象时的编译器优化 7.再次理解封装 8.求12...n(不能使用乘除法、循环、条件判断) 9.计算日期到天数的转换 10.日期差值 11.打印日期 12.累加天数 类与对象下篇&…

数据结构与算法七 堆

一 堆 1.1 堆定义 堆是计算机科学中一类特殊的数据结构的统称&#xff0c;堆通常可以被看做是一棵完全二叉树的数组对象。 堆的特性&#xff1a; 它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其它的每一层从左到右都是满的&#xff0c;如果最后…

4月最新编程排行出炉,第一名ChatGPT都在用~

作为一名合格的&#xff08;准&#xff09;程序员&#xff0c;必做的一件事是关注编程语言的热度&#xff0c;编程榜代表了编程语言的市场占比变化&#xff0c;它的变化更预示着未来的科技风向和机会&#xff01; 快跟着一起看看本月排行有何看点&#xff1a; 4月Tiobe排行榜前…

【CSS】使用 固定定位 实现顶部导航栏 ( 核心要点 | 固定定位元素居中设置 | 代码示例 )

文章目录一、核心要点分析1、顶部导航栏要点2、固定定位垂直居中设置二、代码示例一、核心要点分析 实现下图所示功能 : 上方有一个固定导航栏 , 水平居中设置 ;左右两侧各一个广告栏 , 垂直居中设置 ; 1、顶部导航栏要点 顶部导航栏要点 : 使用固定定位 , 上边偏移设置为 0 …

Linux Ubuntu虚拟机下载安装以及初始配置--VMware、Ubuntu、Xshell、Xftp

一、下载准备 Ubuntu系统下载链接&#xff08;系统本身&#xff09;&#xff1a;官网链接 VMware虚拟机下载链接&#xff08;搭载Ubuntu系统&#xff09;&#xff1a;网盘链接密码XMKD Xshell下载链接&#xff08;虚拟机远程连接&#xff09;&#xff1a;官网链接 Xftp下载…

MySQL索引数据结构入门

之前松哥写过一个 MySQL 系列&#xff0c;但是当时是基于 MySQL5.7 的&#xff0c;最近有空在看 MySQL8 的文档&#xff0c;发现和 MySQL5.7 相比还是有不少变化&#xff0c;同时 MySQL 又是小伙伴们在面试时一个非常重要的知识点&#xff0c;因此松哥打算最近再抽空和小伙伴们…

PyQt5学习笔记一、安装PyQt5和在PyCharm中配置工具

一、安装PyQt5 1. 可以在cmd窗口安装PyQt5和工具 可以在cmd窗口使用命令 pip install PyQt5 安装PyQt5&#xff0c;若指定版本使用命令 pip install PyQt5version&#xff0c;此时同时安装了PyQt5和sip。参考链接 在cmd命令窗口安装Python模块_Mr. 李大白的博客-CSDN博客htt…

potPlay——记忆播放位置、各种快捷键

potPlay——记忆播放位置、各种快捷键potPlay——各种快捷键简洁版完整版快捷键列表potPlay——记忆播放位置potPlay——各种快捷键 简洁版 Q 复位 亮度&#xff0c;对比度&#xff0c;色度复位键 W/E 调暗/调亮 R/T 对比度 Y/U 饱和度 I/O 色彩度 D 上一帧 F 下一帧 M 静音 …

Docker开启并配置远程安全访问

前言 在工作学习中&#xff0c;为了提高项目部署效率&#xff0c;一般会在Idea中直接使用Docker插件连接服务器Docker容器&#xff0c;然后将项目打包与DockerFile一起build成Docker镜像部署运行。但是不可能服务器总是跟着主机的&#xff0c;因此呢时常会面临的一个问题就是从…

【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…