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

责任链模式(ChainOfResponsibilityPattern)属于  行为型模式的一种,将请求沿着一条 传递,直到该上的某个对象处理它为止。

概述

定义如下:一个请求有多个对象来处理,这些对象形成一条链,根据条件确定具体由谁来处理,如果当前对象不能处理则传递给该链中的下一个对象,直到有对象处理它为止。 责任链模式通过将请求和处理分离开来,以进行解耦。职责链模式结构的核心在于引入了一个抽象处理者。

UML结构图

《一起学设计模式 - 责任链模式》

模式结构

  • Handler(抽象处理者): 定义一个处理请求的接口, 提供对后续处理者的引用

  • ConcreteHandler(具体处理者): 抽象处理者的子类,处理用户请求,可选将请求处理掉还是传给下家;在具体处理者中可以访问链中下一个对象,以便请求的转发。

案例

UML图如下:

《一起学设计模式 - 责任链模式》

1.定义 AbstractHandler(抽象处理者) ,使子类形成一条链


  
  
  1. public abstract class AbstractHandler {

  2.    private AbstractHandler handler;

  3.    public abstract void handleRequest(String condition);

  4.    public AbstractHandler getHandler() {

  5.        return handler;

  6.    }

  7.    public void setHandler(AbstractHandler handler) {

  8.        this.handler = handler;

  9.    }

  10. }

2.创建若干个 ConcreteHandler(具体处理者)继承  AbstractHandler,在当前  处理者对象无法处理时,将执行权传给下一个  处理者对象


  
  
  1. public class ConcreteHandlerA extends AbstractHandler {

  2.    @Override

  3.    public void handleRequest(String condition) {

  4.        if (condition.equals("A")) {

  5.            System.out.println("ConcreteHandlerA处理");

  6.        } else {

  7.            System.out.println("ConcreteHandlerA不处理,由其他的Handler处理");

  8.            super.getHandler().handleRequest(condition);

  9.        }

  10.    }

  11. }

  12. public class ConcreteHandlerB extends AbstractHandler {

  13.    @Override

  14.    public void handleRequest(String condition) {

  15.        if (condition.equals("B")) {

  16.            System.out.println("ConcreteHandlerB处理");

  17.        } else {

  18.            System.out.println("ConcreteHandlerB不处理,由其他的Handler处理");

  19.            super.getHandler().handleRequest(condition);

  20.        }

  21.    }

  22. }

  23. public class ConcreteHandlerZ extends AbstractHandler {

  24.    @Override

  25.    public void handleRequest(String condition) {

  26.        //一般是最后一个处理者

  27.        System.out.println("ConcreteHandlerZ处理");

  28.    }

  29. }

3.创建 ChainClient(测试类)

                            
  
  
  1. public class ChainClient {

  2.     public static void main (String [] args ) {

  3.         AbstractHandler handlerA = new ConcreteHandlerA();

  4.         AbstractHandler handlerB = new ConcreteHandlerB();

  5.         AbstractHandler handlerZ = new ConcreteHandlerZ();

  6.         // 如A处理不掉转交给B

  7.        handlerA .setHandler (handlerB );

  8.        handlerB .setHandler (handlerZ );

  9.        handlerA .handleRequest ("Z" );

  10.     }

  11. }

4.运行效果


  
  
  1. ----------------------handleRequest("A")-------------------------

  2. ConcreteHandlerA处理

  3. ----------------------handleRequest("B")-------------------------

  4. ConcreteHandlerA不处理,由其他的Handler处理

  5. ConcreteHandlerB处理

  6. ----------------------handleRequest("Z")-------------------------

  7. ConcreteHandlerA不处理,由其他的Handler处理

  8. ConcreteHandlerB不处理,由其他的Handler处理

  9. ConcreteHandlerZ处理

可以看出,客户端创建了三个 处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,第二个处理者对象的下家是第三个处理者对象。然后客户端将请求传递给第一个处理者对象。

由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理。因此,第一个处理者对象接到请求后,会将请求传递给第二个处理者对象。由于第二个处理者对象没有下家,于是自行处理请求。活动时序图如下所示。

《一起学设计模式 - 责任链模式》

纯与不纯

  • 纯:要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。

  • 不纯:允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。

Filter源码分析

我们经常使用的 Filter就使用到了 责任链模式,创建一个 Filter除了要在应用中做相应配置外,还需要实现  javax .servlet .Filter接口。

                                        
  
  
  1. Configuration

  2. public class MyFilter implements Filter {

  3.     private final static Logger LOGGER = LoggerFactory. getLogger( MyFilter. class);

  4.     @Override

  5.     public void init (FilterConfig filterConfig) throws ServletException {

  6.        LOGGER .info ("init" );

  7.     }

  8.     @Override

  9.     public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException , ServletException {

  10.        LOGGER .info ("doFilter" );

  11.        filterChain .doFilter (servletRequest , servletResponse );

  12.     }

  13.     @Override

  14.     public void destroy () {

  15.        LOGGER .info ("destroy" );

  16.     }

  17. }

使用DEBUG模式所看到的结果如下:

《一起学设计模式 - 责任链模式》

Filter 集

在源码 ApplicationFilterChain中,定义了一个  ApplicationFilterConfig的数组来存放所有的  Filter,使之形成链状

                                                
  
  
  1. public final class ApplicationFilterChain implements FilterChain {

  2.     // 扩容规则

  3.     public static final int INCREMENT = 10;

  4.     // Filter集,默认大小为 0 个  

  5.     private ApplicationFilterConfig[] filters = new ApplicationFilterConfig [0 ];

  6. }

ApplicationFilterConfig 装载规则

在应用首次启动时,会自动实例化对象,并从web应用中读取配置的Filter的信息,然后装进该容器。


  
  
  1. public final class ApplicationFilterConfig implements FilterConfig, Serializable {

  2.    // 省略代码...

  3. }

ApplicationFilterConfig 扩容规则


  
  
  1. //将过滤器添加到在此链条中执行的过滤器集合中。

  2. void addFilter(ApplicationFilterConfig filterConfig) {

  3.    // 防止多次添加相同的过滤器

  4.    for(ApplicationFilterConfig filter:filters)

  5.        if(filter==filterConfig)

  6.            return;

  7.    if (n == filters.length) {

  8.        // 定义一个在原基础之上 +10 的新数组

  9.        ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];

  10.        // 将源数组的元素拷贝到目标数组中去

  11.        System.arraycopy(filters, 0, newFilters, 0, n);

  12.        // 重新赋值

  13.        filters = newFilters;

  14.    }

  15.    //将变量filterConfig放入ApplicationFilterConfig数组中,并将当前过滤器链里面拥有的过滤器数目+1

  16.    filters[n++] = filterConfig;

  17. }

addFilter 的使用


  
  
  1. public static ApplicationFilterChain createFilterChain(ServletRequest request,Wrapper wrapper, Servlet servlet) {

  2.        //如果没有 servlet 要执行,则返回null

  3.        if (servlet == null)

  4.            return null;

  5.        // 创建和初始化过滤器链对象

  6.        ApplicationFilterChain filterChain = null;

  7.        if (request instanceof Request) {

  8.            Request req = (Request) request;

  9.            if (Globals.IS_SECURITY_ENABLED) {

  10.                // 为了安全起见:不要回收

  11.                filterChain = new ApplicationFilterChain();

  12.            } else {

  13.                filterChain = (ApplicationFilterChain) req.getFilterChain();

  14.                if (filterChain == null) {

  15.                    filterChain = new ApplicationFilterChain();

  16.                    req.setFilterChain(filterChain);

  17.                }

  18.            }

  19.        } else {

  20.            // 调度程序在使用中

  21.            filterChain = new ApplicationFilterChain();

  22.        }

  23.        filterChain.setServlet(servlet);

  24.        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

  25.        // 获取过滤器上下文映射

  26.        StandardContext context = (StandardContext) wrapper.getParent();

  27.        FilterMap filterMaps[] = context.findFilterMaps();

  28.        // 如果没有过滤器映射,就认为当前执行完成

  29.        if ((filterMaps == null) || (filterMaps.length == 0))

  30.            return (filterChain);

  31.        // 获取匹配的过滤器映射信息

  32.        DispatcherType dispatcher =

  33.                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

  34.        String requestPath = null;

  35.        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);

  36.        if (attribute != null){

  37.            requestPath = attribute.toString();

  38.        }

  39.        String servletName = wrapper.getName();

  40.        // 将相关路径映射的过滤器添加到此过滤器链中

  41.        for (int i = 0; i < filterMaps.length; i++) {

  42.            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {

  43.                continue;

  44.            }

  45.            if (!matchFiltersURL(filterMaps[i], requestPath))

  46.                continue;

  47.            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)

  48.                context.findFilterConfig(filterMaps[i].getFilterName());

  49.            if (filterConfig == null) {

  50.                // FIXME - log configuration problem

  51.                continue;

  52.            }

  53.            filterChain.addFilter(filterConfig);

  54.        }

  55.        // 添加与servlet名称匹配的筛选器

  56.        for (int i = 0; i < filterMaps.length; i++) {

  57.            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {

  58.                continue;

  59.            }

  60.            if (!matchFiltersServlet(filterMaps[i], servletName))

  61.                continue;

  62.            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)

  63.                context.findFilterConfig(filterMaps[i].getFilterName());

  64.            if (filterConfig == null) {

  65.                // FIXME - log configuration problem

  66.                continue;

  67.            }

  68.            filterChain.addFilter(filterConfig);

  69.        }

  70.        // 返回完整的过滤器链

  71.        return filterChain;

  72.    }

createFilterChain 的调用


  
  
  1. final class StandardWrapperValve extends ValveBase {

  2.    public final void invoke(Request request, Response response) throws IOException, ServletException {

  3.        // 省略代码...

  4.        // 为这个请求创建过滤器链

  5.        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();

  6.        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);

  7.        // 省略代码...

  8.        filterChain.doFilter(request.getRequest(), response.getResponse());

  9.    }

  10. }

执行流程

在StandardWrapperValue类的 invoke()方法中调用ApplicationFilterChai类的  createFilterChain () 方法 在ApplicationFilterChai类的 createFilterChain()方法中调用ApplicationFilterChain类的  addFilter () 方法 在ApplicationFilterChain类的 addFilter()方法中给ApplicationFilterConfig数组赋值。

《一起学设计模式 - 责任链模式》

最后一步

在 doFilter()方法中最后会调用一个  internalDoFilter () 方法,目的就是执行 ApplicationFilterChain中的全部过滤器,从代码中可以发现它调用了  doFilter,而在  doFilter又会调用  internalDoFilter 从而使所有的  Filter都得以调用

                                                        
  
  
  1. private void internalDoFilter( ServletRequest request ,ServletResponse response) throws IOException , ServletException {

  2.     // 如果存在下一个,继续调用下一个过滤器

  3.     if (pos < n ) {

  4.         ApplicationFilterConfig filterConfig = filters [pos ++];

  5.         try {

  6.             Filter filter = filterConfig .getFilter ();

  7.             if (request .isAsyncSupported () && "false". equalsIgnoreCase(

  8.                    filterConfig .getFilterDef ().getAsyncSupported ())) {

  9.                request .setAttribute (Globals .ASYNC_SUPPORTED_ATTR , Boolean. FALSE);

  10.             }

  11.             if( Globals .IS_SECURITY_ENABLED ) {

  12.                 final ServletRequest req = request ;

  13.                 final ServletResponse res = response ;

  14.                 Principal principal =

  15.                     ((HttpServletRequest ) req ).getUserPrincipal ();

  16.                 Object[] args = new Object []{req , res , this};

  17.                 SecurityUtil. doAsPrivilege ( "doFilter", filter, classType, args, principal);

  18.             } else {

  19.                 // 此处调用Filter的doFilter()方法  / 而 doFilter 又会调用 internalDoFilter 直到调用完所有的过滤器

  20.                filter .doFilter (request , response , this);

  21.             }

  22.         } catch (IOException | ServletException | RuntimeException e) {

  23.             throw e ;

  24.         } catch (Throwable e) {

  25.            e = ExceptionUtils. unwrapInvocationTargetException( e);

  26.             ExceptionUtils. handleThrowable( e);

  27.             throw new ServletException( sm. getString( "filterChain.filter"), e);

  28.         }

  29.         return;

  30.     }

  31.     // 从最后一个开始调用

  32.     try {

  33.         if (ApplicationDispatcher .WRAP_SAME_OBJECT ) {

  34.            lastServicedRequest .set (request );

  35.            lastServicedResponse .set (response );

  36.         }

  37.         if (request .isAsyncSupported () && !servletSupportsAsync ) {

  38.            request .setAttribute (Globals .ASYNC_SUPPORTED_ATTR ,

  39.                     Boolean. FALSE);

  40.         }

  41.         // 包装请求

  42.         if ((request instanceof HttpServletRequest) &&

  43.                 (response instanceof HttpServletResponse) &&

  44.                 Globals. IS_SECURITY_ENABLED ) {

  45.             final ServletRequest req = request ;

  46.             final ServletResponse res = response ;

  47.             Principal principal =

  48.                 ((HttpServletRequest ) req ).getUserPrincipal ();

  49.             Object[] args = new Object []{req , res };

  50.             SecurityUtil. doAsPrivilege( "service",

  51.                                       servlet ,

  52.                                       classTypeUsedInService ,

  53.                                       args ,

  54.                                       principal );

  55.         } else {

  56.            servlet .service (request , response );

  57.         }

  58.     } catch (IOException | ServletException | RuntimeException e) {

  59.         throw e ;

  60.     } catch (Throwable e) {

  61.        e = ExceptionUtils. unwrapInvocationTargetException( e);

  62.         ExceptionUtils. handleThrowable( e);

  63.         throw new ServletException( sm. getString( "filterChain.servlet"), e);

  64.     } finally {

  65.         if (ApplicationDispatcher .WRAP_SAME_OBJECT ) {

  66.            lastServicedRequest .set (null );

  67.            lastServicedResponse .set (null );

  68.         }

  69.     }

  70. }

总结

职责链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用职责链模式,例如在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。

优点

  • 降低耦合度,分离了请求与处理,无须知道是哪个对象处理其请求

  • 简化对象的相互连接,仅保持一个指向后者的引用,而不需保持所有候选接受者的引用

  • 扩展容易,新增 具体请求处理者,只需要在客户端重新建链即可,无需破坏原代码

缺点

  • 如果请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理

  • 较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

  • 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。

金无足赤,人无完人。就像所有的设计模式一样,有优点优缺点,但是总的来说优点必定大于缺点或者说缺点相对于优点来说更可控。

说点什么

参考文献:http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html

全文代码:https://gitee.com/battcn/design-pattern/tree/master/Chapter11/battcn-chain-responsibility

福利

关注公众号: battcn,回复  springboot即可获得  <Spring Boot 从入门到实战 基础实战系列教程全集> 与  <2017最新spring boot 外卖实战微信公众平台视频教程>

    原文作者:算法小白
    原文地址: https://juejin.im/entry/5afa3a715188254267263182
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞