文章目录

  • 1. 责任链模式
    • 1.1 责任链模式简介
    • 1.2 责任链模式结构
    • 1.3 责任链模式示例
  • 2. 责任链模式在源码中的应用
    • 2.1 Servlet 中的责任链模式
    • 2.2 Spring 中的责任链模式
  • 3. 总结

俗话说没有规矩不成方圆,我们无论在工作还是生活中很多事情都需要按照规定的流程办事,这样的流程往往都是环环相扣的,上一部完成之后才会流转到下一步执行。比如我们在做饭时都是先买菜、洗菜、切菜、炒菜、装盘在这样的过程中只有上一步完成之后才能开始下一步最后得到一道做好的菜;又比如在晋升提名时,首先我们要做一个述职报告进行述职,然后就是评审小组打分,评审小组筛选通过后,流转到项目组领导处审批,项目组领导根据述职报告和评审小组分数决定是否晋升,项目组领导同意之后最后流转到部门领导审批并给出最后结果。像这种一步一步完成流程都可以通过责任链模式来实现。

1. 责任链模式

1.1 责任链模式简介

责任链模式顾名思义是将不同职责的步骤串联起来执行,并且一个步骤执行完成之后才能够执行下一个步骤。从名字可以看出通常责任链模式使用链表来完成。因此当执行任务的请求发起时,从责任链上第一步开始往下传递,直到最后一个步骤完成。在责任链模式当中,客户端只用执行一次流程开始的请求便不再需要参与到流程执行当中,责任链上的流程便能够自己一直往下执行,客户端同样也并不关心执行流程细节,从而实现与流程之间的解耦。

1.2 责任链模式结构

责任链模式需要有以下几个角色:

  • 抽象处理器(Handler):处理器抽象接口,定义了处理请求的方法和执行下一步处理的处理器
  • 具体处理器(ConcreteHandler):执行请求的具体实现,先根据请求执行处理逻辑,完成之后将请求交给下一个处理器执行
  • 调用者:调用者通过创建处理器并将请求交给处理器进行处理

代码如下:

// 抽象处理器
public abstract class Handler {private Handler next;public Handler getNext() {return next;}public void setNext(Handler next) {this.next = next;}public abstract void handle(Object request);
}// 具体处理器 1
public class ConcreteHandler1 extends Handler {@Overridepublic void handle(Object request) {System.out.println("concrete handler 1 execute request. request: " + request);if (getNext() != null) {getNext().handle(request);}}
}// 具体处理器 2
public class ConcreteHandler2 extends Handler {@Overridepublic void handle(Object request) {System.out.println("concrete handler 2 execute request. request: " + request);if (getNext() != null){getNext().handle(request);}}
}// 具体处理器 3
public class ConcreteHandler3 extends Handler {@Overridepublic void handle(Object request) {System.out.println("concrete handler 3 execute request. request: " + request);if (getNext() != null) {getNext().handle(request);}}
}

调用者执行代码:

public static void main(String[] args) {Handler concreteHandler1 = new ConcreteHandler1();Handler concreteHandler2 = new ConcreteHandler2();Handler concreteHandler3 = new ConcreteHandler3();concreteHandler1.setNext(concreteHandler2);concreteHandler2.setNext(concreteHandler3);concreteHandler1.handle("my request.");
}

从上面的代码我们可以看到其实责任链模式是非常简单的,但是其中有几个点需要注意一下

  • 首先我们需要对整个责任链进行初始化,即设置每个处理器的 next
  • 在每个具体处理器处理完之后需要手动调用下一个处理器的 handle 方法来执行下一步处理,这里其实还可以使用模板方法模式进行优化

执行上面代码,控制台输出如下:

concrete handler 1 execute request. request: my request.
concrete handler 2 execute request. request: my request.
concrete handler 3 execute request. request: my request.

1.3 责任链模式示例

某一天你收到面试邀请,于是需要向你的领导请假去面试,你便会提出请假的申请。请假申请会先到你的直属 leader 处审批,审批通过后再到部门 leader 处审批,部门 leader 通过后,最后到人事处报备记录请假天数。如果在传统企业里面,我们需要手写一份请假表,然后跑到直属 leader 办公室,让直属 leader 签字,然后再到部门 leader 办公室签字,最后还要跑到人事处上交请假单,这样相当于发出了三次请求,才能走完整个请假流程。

但是在现代各种 OA 系统管理下,整个请假流程就变的简单了,我们只需要发起一次请假请求,接下来你的请假请求便会自动的在审批人中间进行流转,这个时候我们的责任链模式便派上用场。代码如下:

// 请假抽象处理器
public abstract class DayOffHandler {private DayOffHandler next;public DayOffHandler getNext() {return next;}public void setNext(DayOffHandler next) {this.next = next;}public abstract void handle(String request);}
// 直属 leader 处理
public class GroupLeaderHandler extends DayOffHandler {@Overridepublic void handle(String request) {System.out.println("直属 leader 审查: " + request);System.out.println("同意请求");if (getNext() != null) {getNext().handle(request);}}
}
// 部门 leader 处理
public class DepartmentLeaderHandler extends DayOffHandler{@Overridepublic void handle(String request) {System.out.println("部门 leader 审查: " + request);System.out.println("同意请求");if (getNext() != null) {getNext().handle(request);}}
}
// 人事处处理
public class HRHandler extends DayOffHandler {@Overridepublic void handle(String request) {System.out.println("人事处审查: " + request);System.out.println("同意请求,记录请假");if (getNext() != null) {getNext().handle(request);}}
}

上面的代码定义了请假抽象处理类和三个具体的处理人,我们需要将这三个处理人的流程初始化串联起来,并且一步步的执行下去,像下面这张图所示流程一样,于是客户端的代码如下:

public static void main(String[] args) {DayOffHandler groupLeaderHandler = new GroupLeaderHandler();DayOffHandler departmentLeaderHandler = new DepartmentLeaderHandler();DayOffHandler hrHandler = new HRHandler();groupLeaderHandler.setNext(departmentLeaderHandler);departmentLeaderHandler.setNext(hrHandler);System.out.println("收到面试通知,需要请假");String request = "家中有事,请假半天,望批准";System.out.println("发起请求:");groupLeaderHandler.handle(request);
}

从客户端代码中可以看到,我们首先实例化了三个具体处理人,然后通过 setNext 方法将他们串联起来,而我们只需向直属 leader 发起一次请假请求即可,整个审批流程便能够自动的执行下去,不需要再挨个跑办公室申请了。执行后的结果如下:

收到面试通知,需要请假
发起请求:
直属 leader 审查: 家中有事,请假半天,望批准
同意请求
部门 leader 审查: 家中有事,请假半天,望批准
同意请求
人事处审查: 家中有事,请假半天,望批准
同意请求,记录请假

2. 责任链模式在源码中的应用

2.1 Servlet 中的责任链模式

说到责任链模式,那么最著名的当然是 Servlet 中的过滤器 Filter 了,在这拦截器和过滤器的体系中都使用责任链模式来依次处理每个请求,首先看看过滤器 Filter 的使用方式。Filter 接口如下:

public interface Filter {default void init(FilterConfig filterConfig) throws ServletException {}void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;default void destroy() {}
}

FilterChain 便是过滤器 Filter 的一条责任链,其代码如下:

public interface FilterChain {void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}public final class ApplicationFilterChain implements FilterChain {public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request;final ServletResponse res = response;try {AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {public Void run() throws ServletException, IOException {ApplicationFilterChain.this.internalDoFilter(req, res);return null;}});} catch (PrivilegedActionException var7) {Exception e = var7.getException();if (e instanceof ServletException) {throw (ServletException)e;}if (e instanceof IOException) {throw (IOException)e;}if (e instanceof RuntimeException) {throw (RuntimeException)e;}throw new ServletException(e.getMessage(), e);}} else {this.internalDoFilter(request, response);}}private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (this.pos < this.n) {ApplicationFilterConfig filterConfig = this.filters[this.pos++];try {Filter filter = filterConfig.getFilter();if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);}if (Globals.IS_SECURITY_ENABLED) {Principal principal = ((HttpServletRequest)request).getUserPrincipal();Object[] args = new Object[]{request, response, this};SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);} else {filter.doFilter(request, response, this);}} catch (ServletException | RuntimeException | IOException var15) {throw var15;} catch (Throwable var16) {Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.filter"), e);}} else {try {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() && !this.servletSupportsAsync) {request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);}if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {Principal principal = ((HttpServletRequest)request).getUserPrincipal();Object[] args = new Object[]{request, response};SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);} else {this.servlet.service(request, response);}} catch (ServletException | RuntimeException | IOException var17) {throw var17;} catch (Throwable var18) {Throwable e = ExceptionUtils.unwrapInvocationTargetException(var18);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.servlet"), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set((Object)null);lastServicedResponse.set((Object)null);}}}}
}

在 internalDoFilter() 方法中,可以看到整个 FilterChain 上使用数组 filters 存放每一个过滤器及其配置并使用 pos 记录当前遍历到哪一个过滤器,然后再执行获取到的 Filter 的 doFilter 方法。与前面所讲链表方式存放不同,这里的链路使用数组来进行存放。

2.2 Spring 中的责任链模式

在 SpringMVC 中的 Interceptor 同样也用到了责任链模式。首先来看看 Interceptor 的抽象处理类

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 {}
}

在抽象处理类中,定义了三个方法,分别是处理前置处理器、后置处理器和整个流程完成之后的处理。通过 HandlerExecutionChain 将拦截器串联起来,在 HandlerExecutionChain 中,我们需要关注 applyPreHandle、applyPostHandle 和 triggerAfterCompletion 三个方法,这三个方法分别执行了拦截器中所定义的 preHandle 、postHandle 和 afterCompletion 方法。并且从代码中也能够看处,和前面的过滤器一样,所有的拦截器都存放在 interceptors 数组中,并在三个方法中遍历 interceptors 数组依次执行相应的方法。

public class HandlerExecutionChain {@Nullableprivate HandlerInterceptor[] interceptors;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;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable 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);}}}void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);}}}}
}

3. 总结

责任链模式也是常见的设计模式,各个不同职责的处理器串联起来,通过一次请求便能够执行完每个处理器的处理方法。通过这样的方式请求的发送者只需发出一次请求同时也不需要知道详细的链路结构;而请求的接送方只关心自己的处理逻辑,自己处理完成之后将请求传递给下一个接收者,从而完成自己的任务,这样便实现了请求发送者和接收者的解耦。而从源码分析中可以看到,责任链模式虽然常见使用链表结构,但是使用数组和列表同样能够完成需求。

【深入设计模式】责任链模式—责任链模式及责任链模式在源码中的应用相关推荐

  1. 【设计模式系列20】解释器模式原理及其在JDK和Spring源码中的体现

    解释器模式原理及其在JDK源码中的体现 设计模式系列总览 前言 什么是解释器模式 终结符表达式和非终结符表达式 解释器模式示例 解释器模式角色 解释器模式在JDK和Spring源码中应用 解释器模式应 ...

  2. 设计模式 笔记4 | 简单工厂模式 在源码中的应用 | Calendar 日历 | 源码浅析 | 使用总结 | 建造者模式

    文章目录 一.Calendar 日历类 1.1 内部属性 1.2 设置时间属性值 1.3 获取时间属性 1.4 使用 Calander 计算时间 二.Calender 类中的设计模式 2.1 简单工厂 ...

  3. 装饰器模式在MyBatis以及Spring源码中的应用

    结构型模式                 ----顺口溜:适装桥组享代外 目录 1.装饰器模式 1.1 装饰器模式UML图 1.2 日常生活中看装饰器模式 1.3 使用场景 1.4 Java代码实现 ...

  4. android 指令模式,Android 源码中的命令模式

    原标题:Android 源码中的命令模式 (点击上方公众号,可快速关注) 来源:伯乐在线专栏作者 - PleaseCallMeCoder 链接:http://android.jobbole.com/8 ...

  5. 装饰者模式在源码中的应用

    装饰器模式在源码中也应用得非常多,在JDK 中体现最明显的类就是IO 相关的类,如BufferedReader.InputStream.OutputStream,看一下常用的InputStream 的 ...

  6. 快速理解工厂方法模式,及其在源码中的应用

    (一).什么是工厂方法模式 有一个总工厂负责生产各种电视产品,此时来了一个生产小米电视的需求,就会分配出一个制作小米电视的工厂,然后就能用该工厂生产小米电视了. 上面这种通过工厂来实例化类的方式抽象到 ...

  7. JDK源码解析 迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。

    JDK源码解析 迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的. 看完这段代码是不是很熟悉,与我们上面代码基本类似.单列集合都使用到了迭代器,我们以Arr ...

  8. 策略模式在JDK 源码中的体现

    首先来看一个比较常用的比较器Comparator 接口,我们看到的一个大家常用的compare()方法,就是一个策略抽象实现: Comparator 抽象下面有非常多的实现类,我们经常会把Compar ...

  9. 记一次linux下,源码中包含外链时,SVN打tag

    问题描述:源码中包含外链打tag,使用SVN cp 源码文件  SVN上的tag目录 :打tag时会将源码的外链的属性也保存下来,导致打完tag后外链目录仍然在一直更新,导致tag目录无效 解决方法 ...

最新文章

  1. 2007年3月东北微软技术活动预告
  2. 排序算法java源代码_排序算法汇总(java实现,附源代码)
  3. java线程学习之notify方法和notifyAll方法
  4. 常见网络游戏同步方式
  5. Spring框架学习笔记(7)——代理对象实现AOP
  6. python无实际意义的语句_没有学不会的python--认识简单的数据类型
  7. 【训练过程】2) Train the VAEs of domain A and domain B respectively(分别训练域A和域B的VAE)
  8. 【Spring Cloud】注册中心-Nacos
  9. python verilog就业_Verilog会被淘汰吗?
  10. c语言实现sbrk函数,菜鸟随笔(2)---brk()与sbrk()函数的学习与使用
  11. excel如何把顺序倒过来_如何在筛选后的表里复制粘贴数据到同行
  12. 深入剖析Redis主从复制
  13. ios查看帧率的软件_iOS显示当前页面帧数
  14. LibPcap丢包问题
  15. 趣图:gif PostgreSQL MySQL 从删库到跑路
  16. vue 生成二维码海报并进行微信分享
  17. 安卓开发必备知识体系:Java篇
  18. 无传感器永磁同步电机电机自适应自抗扰ADRC控制策略
  19. ReadProcessMemory函数的用法
  20. ECNU计科复试机试(2019)

热门文章

  1. 计算机制作灯笼,电脑怎么做灯笼
  2. 全志F133A/B开发板
  3. js对数组进行操作实现购物车效果
  4. 哪些浏览器功能泄漏浏览器隐私
  5. mdp框架_MDP实现细节(一)-- 贝尔曼方程
  6. jq获取兄弟节点_jquery 获取元素(父节点,子节点,兄弟节点)
  7. jquery操作兄弟节点
  8. linux 连接宽带
  9. Mysql备份——mysqldump
  10. 【qq机器人】欢迎加群通报