责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

一、责任链模式介绍

其实在日常生活中,有不少使用责任链的场景。比如公司采购审批流程,需要各个部门领导的批准同意。在责任链模式中,客户端只需要将请求发送到责任链上,无须关心请求的处理细节和传递,请求会自动进行传递。

1.1 责任链模式的结构

责任链模式的结构大致如下所示:

  • abstract Handler:抽象处理者,定义一个处理请求的接口,内部包含抽象处理方法和后继具体处理者
  • Handler1、Handler2:具体处理者,具体实现抽象处理者的方法,并对请求做一些逻辑处理
  • Client:客户端,使用职责链模式

1.2 责任链模式的实现

根据上面的类图,可以实现如下代码:

/*** @description: 抽象处理类* @author: wjw* @date: 2022/4/4*/
public abstract class Handler {private Handler successor;public Handler getSuccessor() {return successor;}public void setSuccessor(Handler successor) {this.successor = successor;}/*** 处理请求的抽象方法* @param request 请求*/public abstract void handleRequest(String request);
}
/*** @description: 具体处理者1* @author: wjw* @date: 2022/4/4*/
public class Handler1 extends Handler{private String handler;public Handler1(String handler) {this.handler = handler;}@Overridepublic void handleRequest(String request) {if ("handler1".equals(request)) {System.out.println("具体处理者handler1进行请求处理");} else {if (getSuccessor() != null) {//如果指向下一个具体处理者getSuccessor().handleRequest(request);} else {System.out.println("没有处理者进行处理");}}}
}
/*** @description: 具体处理者2* @author: wjw* @date: 2022/4/4*/
public class Handler2 extends Handler{private String handler;public Handler2(String handler) {this.handler = handler;}@Overridepublic void handleRequest(String request) {if ("handler2".equals(request)) {System.out.println("具体处理者handler2进行请求处理");} else {if (getSuccessor() != null) {getSuccessor().handleRequest(request);} else {System.out.println("请求没有被任何处理者处理");}}}
}
/*** @description: 客户端类* @author: wjw* @date: 2022/4/4*/
public class Client {public static void main(String[] args) {Handler handler1 = new Handler1("handler1");Handler handler2 = new Handler2("handler2");handler1.setSuccessor(handler2);handler1.handleRequest("handler1");handler1.handleRequest("handler2");}
}

测试结果:

具体处理者handler1进行请求处理
具体处理者handler2进行请求处理

二、责任链模式的应用场景

职责链模式最常见的应用就是用来开发各种框架的过滤器和拦截器,比如Spring Interceptor和Servlet Filter

2.1 在Servlet Filter中的应用

Filter 可以实现对HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。比如一些Servlet 容器(TomCat、Jetty等)就支持Filter的过滤功能。以TomCat 为例:

所以当Servlet 请求到来时,首先会经过Filter 处理,最后再到达Servlet实例。我这里选取的TomCat版本是SpringBoot自带的9.0,先来看看FilterChain 的接口:

public interface FilterChain {//Filter具体实现void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

ApplicationFilterChain是责任链模式的具体实现类:

public final class ApplicationFilterChain implements FilterChain {private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; //Filter配置数组private int pos = 0; //执行Filter的序号private int n = 0;     //目前Filter的个数private Servlet servlet = null;public ApplicationFilterChain() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {//Filter 具体实现//...}public static ServletRequest getLastServicedRequest() {return (ServletRequest)lastServicedRequest.get();}public static ServletResponse getLastServicedResponse() {return (ServletResponse)lastServicedResponse.get();}void addFilter(ApplicationFilterConfig filterConfig) {ApplicationFilterConfig[] newFilters = this.filters;int var3 = newFilters.length;for(int var4 = 0; var4 < var3; ++var4) {ApplicationFilterConfig filter = newFilters[var4];if (filter == filterConfig) {return;}}//增加Filterif (this.n == this.filters.length) {newFilters = new ApplicationFilterConfig[this.n + 10];System.arraycopy(this.filters, 0, newFilters, 0, this.n);this.filters = newFilters;}this.filters[this.n++] = filterConfig;}
}

2.2 在Spring Interceptor中的应用

和Servlet Filter类似,在Spring 中也有对应的过滤器 Interceptor。它是由Spring MVC 框架来实现,借一张来自《设计模式之美》的图片来说明:

客户端发送请求,首先会经过Servlet Filter,然后再经过Spring Interceptor,最后再到达具体的业务中。

和Filter一样,Interceptor 中也是基于责任链模式来实现的,与之相对的HandlerInterceptor是抽象处理接口:

public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}

会有各种具体处理类的实现:

此外在其他框架中也存在着职责链模式的使用,比如Dubbo Filter、Netty ChannelPipeline等等

三、责任链模式实战

模拟在618大促期间的业务系统上线审批流程场景:

像是这些⼀线电商类的互联⽹公司,阿⾥、京东、拼多多等,在618期间都会做⼀些运营活动场景以及 提供的扩容备战,就像过年期间百度的红包⼀样。但是所有开发的这些系统都需要陆续的上线,因为临 近618有时候也有⼀些紧急的调整的需要上线,但为了保障线上系统的稳定性是尽可能的减少上线的, 也会相应的增强审批⼒度。就像⼀级响应、⼆级响应⼀样。

⽽这审批的过程在随着特定时间点会增加不同级别的负责⼈加⼊,每个⼈就像责任链模式中的每⼀个核 ⼼点。对于研发并不需要关⼼具体的审批流程处理细节,只需要知道这个上线更严格,级别也更 ⾼,但对于研发⼈员来说同样是点击相同的提审按钮,等待审核。

使用责任链模式可以将各个服务模块按照一、二、三级进行分离,每个级别可以像Filter一样用Successor下一个级别的方法进行调用。具体结构图如下(来自《重学Java设计模式》)

具体代码结构

│——AuthInfo.java
│——AuthLink.java
│
├─business
│     AuthService.java
│
└─implLevel1AuthLink.javaLevel2AuthLink.javaLevel3AuthLink.java

实现代码:

/*** @description: 链路抽象审批* @author: wjw* @date: 2022/4/4*/
public abstract class AuthLink {/**时间格式化**/protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");/**级别人员ID**/protected String levelUserId;/**级别人员姓名**/protected String levelUserName;/**下一个链路**/private AuthLink next;public AuthLink(String levelUserId, String levelUserName) {this.levelUserId = levelUserId;this.levelUserName = levelUserName;}public AuthLink next() {return next;}public AuthLink appendNext(AuthLink next) {this.next = next;return this;}/**每个具体处理者必须实现的方法**/public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);
}
/*** @description: 责任链中返回对象的定义* @author: wjw* @date: 2022/4/4*/
public class AuthInfo {private String code;private String info = " ";public AuthInfo(String code, String... infos) { //...表示可变长参数,可以传入多个参数this.code = code;for (String s : infos) {this.info = this.info.concat(s);}}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}
}
/*** @description: 一级审核人* @author: wjw* @date: 2022/4/4*/
public class Level1AuthLink extends AuthLink {public Level1AuthLink(String levelUserId, String levelUserName) {super(levelUserId, levelUserName);}@Overridepublic AuthInfo doAuth(String uId, String orderId, Date authDate) {Date date = AuthService.queryAuthInfo(levelUserId, orderId);if (null == date) {return new AuthInfo("0001", "单号", orderId, "状态:一级审批负责人", levelUserName);}AuthLink next = super.next();if (null == next) {return new AuthInfo("0000", "单号", orderId, "状态:一级审批负责人", " 时间:", f.format(date), "审批人:", levelUserName);}return next.doAuth(uId, orderId, authDate);}
}
/*** @description: 测试类* @author: wjw* @date: 2022/4/5*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test_AuthLink() throws ParseException {AuthLink authLink = new Level3AuthLink("1000013", "王工").appendNext(new Level2AuthLink("1000012", "张经理")).appendNext(new Level3AuthLink("1000011", "赵总裁"));logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));AuthService.auth("1000013", "100099800423123");logger.info("测试结果:{}", "模拟二级审批,王工");logger.info("测试结果:{}",JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));AuthService.auth("1000012", "100099800423123");logger.info("测试结果:{}", "模拟二级负责人审批,张经理");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));AuthService.auth("1000011", "100099800423123");logger.info("测试结果:{}", "模拟二级负责人审批,赵总裁");logger.info("测试结果:{}", JSON.toJSONString(authLink.doAuth("ethan", "100099800423123", new Date())));}
}

最后的测试结果:

10:45:14.496 [main] INFO  ApiTest - 测试结果:{"code":"0001","info":" 单号:100099800423123 状态:待三级审批 王工"}
10:45:14.500 [main] INFO  ApiTest - 测试结果:模拟二级审批,王工
10:45:14.500 [main] INFO  ApiTest - 测试结果:{"code":"0001","info":" 单号:100099800423123 状态:待三级审批 赵总裁"}
10:45:14.500 [main] INFO  ApiTest - 测试结果:模拟二级负责人审批,张经理
10:45:14.500 [main] INFO  ApiTest - 测试结果:{"code":"0001","info":" 单号:100099800423123 状态:待三级审批 赵总裁"}
10:45:14.500 [main] INFO  ApiTest - 测试结果:模拟二级负责人审批,赵总裁
10:45:14.500 [main] INFO  ApiTest - 测试结果:{"code":"0000","info":" 单号: 100099800423123 状态:三级审批完成 时间:2022-04-05 10:45:14 审批人:赵总裁"}

参考资料

《设计模式之美》

http://c.biancheng.net/view/4024.html

《重学Java设计模式》

责任链模式实现及在Filter中的应用相关推荐

  1. 责任链模式在复杂数据处理场景中的实战

    相信大家在日常的开发中都遇到过复杂数据处理和复杂数据校验的场景,本文从一线开发者的角度,分享了责任链模式在这种复杂数据处理场景下的实战案例,此外,作者在普通责任链模式的基础上进行了升级改造,可以适配更 ...

  2. java设计模式之责任链模式以及在java中作用

    责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个 ...

  3. 责任链模式之Tomcat Filter应用

    责任链模式之Tomcat Filter应用 责任链模式是一种对象的行为型模式. 其定义为:很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象处理此请求.发 ...

  4. 一起学设计模式 - 责任链模式

    责任链模式(ChainOfResponsibilityPattern)属于 行为型模式的一种,将请求沿着一条链传递,直到该链上的某个对象处理它为止. 概述 定义如下:一个请求有多个对象来处理,这些对象 ...

  5. [转]《JAVA与模式》之责任链模式

    http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html 在阎宏博士的<JAVA与模式>一书中开头是这样描述责 ...

  6. 《JAVA与模式》之责任链模式

    2019独角兽企业重金招聘Python工程师标准>>> 详细请访问原博客:http://www.cnblogs.com/java-my-life/archive/2012/05/28 ...

  7. 传统责任链模式和变种责任链模式

    一.传统责任链模式 传统责任链模式是事件在链条中的某一节进行处理,而变种责任链模式是事件会在链条的每一节都进行处理,每一节担当的责任各不相同. 1.一个接口或抽象类 定义一个接口,一个方法,判断数值是 ...

  8. 23种设计模式——责任链模式

    一.责任链模式 顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的 ...

  9. 责任链模式(Chain of Responsibility) Java实现

    责任链模式 责任链模式(Chain of Responsibility)定义 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递 ...

  10. 责任链模式(Chain of Responsibility模式)

    在现实生活中,一个事件需要经过多个对象处理是很常见的场景.例如,采购审批流程.请假流程等.公司员工请假,可批假的领导有部门负责人.副总经理.总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的 ...

最新文章

  1. 判断无线网卡是否支持5GHz频段
  2. STL 之vector详解
  3. ajax php 区别,PHP中AJAX比较(转)
  4. React Axios的定义,以及使用方式
  5. 如何控制让PO的数量不超过PR数量
  6. 【译】Why Decentralized AI Matters Part I: Economics and Enablers
  7. Linux2.6内核驱动与2.4的区别 .
  8. 求n!,C(n,m)和A(n,m)最后的非零位。
  9. 让一个图片填满一个控件_如何在Android中实现一个全景图控件(二)
  10. qt c++ 图片预览_这是Google Pixel 4上的新动态壁纸的预览
  11. COM应用实例--获取桌面墙纸路径
  12. 199. Binary Tree Right Side View
  13. 能搞垮你的不止是同行
  14. centos7 mysql添加密码_centos-在Centos7上更改mysql根密码
  15. 基于STM32单片机的远程智能浇花花盆GSM短信浇水补光方案原理图程序设计
  16. Android 解析Excel (xls格式)
  17. 简单的android小程序计算机,Android实现简易计算器小程序
  18. 计算机调查应用表格,大学计算机实验课_调查报告_表格模板_应用文书.doc
  19. 微信小程序 async await解决异步问题
  20. 去除桌面应用程序快捷方式的图标

热门文章

  1. 计算机排版系统程序,《计算机排版系统.doc
  2. 用python画图的好处_用Python绘图,感受编程之美
  3. QtDesigner配置
  4. vim 插件安装 (ubuntu OS)
  5. python 怎么将数字转大写_python 数字转换为大写
  6. 3DMax 安装 超图 插件
  7. Java项目:Springboot校园报修管理系统
  8. 【HTML/JS】百度地图javascriptAPI点击地图得到坐标(拾取坐标) 标签: 百度地图坐标
  9. js植物大战僵尸实训1
  10. somachineV4.1的注册