乐趣区

关于责任链模式:一文带你读懂设计模式之责任链模式-京东云技术团队

1. 前言

emm,翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候已经专门学习并整顿过过设计模式中的 责任链模式,之前只是对其简略理解过罕用的设计模式有哪些,并未联合实例和源码深刻对其探索,利用相熟代码契机进行零碎学习并整顿文档如下。

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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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.



3. 具体实例 demo

日常销假 为例。销假申请会先到你的 直属 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 {
    @Override
    public void handle(String request) {System.out.println("直属 leader 审查:" + request);
        System.out.println("批准申请");
        if (getNext() != null) {getNext().handle(request);
        }
    }
}
// 部门 leader 解决
public class DepartmentLeaderHandler extends DayOffHandler{
    @Override
    public void handle(String request) {System.out.println("部门 leader 审查:" + request);
        System.out.println("批准申请");
        if (getNext() != null) {getNext().handle(request);
        }
    }
}
// 人事处解决
public class HRHandler extends DayOffHandler {
    @Override
    public 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 审查: 家中有事,销假半天,望批准
批准申请
人事处审查: 家中有事,销假半天,望批准
批准申请,记录销假



4. 责任链模式在源码中的利用

说到责任链模式,那么最驰名的当然是 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 记录 以后遍历到哪一个过滤器,而后再执行获取到的 FilterdoFilter 办法。与后面所讲链表形式寄存不同,这里的链路应用数组来进行寄存。
  • 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 中,咱们须要关注 applyPreHandleapplyPostHandletriggerAfterCompletion 三个办法,这三个办法别离执行了拦截器中所定义的 preHandlepostHandleafterCompletion 办法。并且从代码中也可能看处,和后面的过滤器一样,所有的拦截器都寄存在 interceptors 数组中,并在三个办法中遍历 interceptors 数组顺次执行相应的办法

public class HandlerExecutionChain {
    @Nullable
    private 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);
                }
            }
        }

    }
}



5. 总结

责任链模式也是常见的设计模式,各个不同职责的处理器串联起来,通过一次申请便可能执行完每个处理器的解决办法。通过这样的形式申请的发送者只需收回一次申请同时也不须要晓得具体的链路构造;而申请的接送方只关怀本人的解决逻辑,本人解决实现之后将申请传递给下一个接收者,从而实现本人的工作,这样便实现了申请发送者和接收者的解耦。而从源码剖析中能够看到,责任链模式尽管常见应用链表构造,然而应用数组和列表同样可能实现需要。

作者:京东科技 宋慧超

起源:京东云开发者社区

退出移动版