一塊兒學設計模式 - 責任鏈模式

責任鏈模式(Chain Of Responsibility Pattern)屬於行爲型模式的一種,將請求沿着一條傳遞,直到該上的某個對象處理它爲止。html

<!-- more -->java

概述

定義以下:一個請求有多個對象來處理,這些對象造成一條鏈,根據條件肯定具體由誰來處理,若是當前對象不能處理則傳遞給該鏈中的下一個對象,直到有對象處理它爲止。責任鏈模式經過將請求和處理分離開來,以進行解耦。職責鏈模式結構的核心在於引入了一個抽象處理者。git

UML結構圖web

責任鏈模式UML圖

模式結構spring

  • Handler(抽象處理者): 定義一個處理請求的接口,提供對後續處理者的引用
  • ConcreteHandler(具體處理者): 抽象處理者的子類,處理用戶請求,可選將請求處理掉仍是傳給下家;在具體處理者中能夠訪問鏈中下一個對象,以便請求的轉發。

案例

UML圖以下:設計模式

UML圖

1.定義AbstractHandler(抽象處理者),使子類造成一條鏈數組

public abstract class AbstractHandler {

    private AbstractHandler handler;

    public abstract void handleRequest(String condition);

    public AbstractHandler getHandler() {
        return handler;
    }

    public void setHandler(AbstractHandler handler) {
        this.handler = handler;
    }
}

2.建立若干個ConcreteHandler(具體處理者)繼承AbstractHandler,在當前處理者對象沒法處理時,將執行權傳給下一個處理者對象安全

public class ConcreteHandlerA extends AbstractHandler {

    @Override
    public void handleRequest(String condition) {
        if (condition.equals("A")) {
            System.out.println("ConcreteHandlerA處理");
        } else {
            System.out.println("ConcreteHandlerA不處理,由其餘的Handler處理");
            super.getHandler().handleRequest(condition);
        }
    }
}

public class ConcreteHandlerB extends AbstractHandler {

    @Override
    public void handleRequest(String condition) {
        if (condition.equals("B")) {
            System.out.println("ConcreteHandlerB處理");
        } else {
            System.out.println("ConcreteHandlerB不處理,由其餘的Handler處理");
            super.getHandler().handleRequest(condition);
        }
    }
}

public class ConcreteHandlerZ extends AbstractHandler {
    @Override
    public void handleRequest(String condition) {
        //通常是最後一個處理者
        System.out.println("ConcreteHandlerZ處理");
    }
}

3.建立ChainClient(測試類)springboot

public class ChainClient {

    public static void main(String[] args) {
        AbstractHandler handlerA = new ConcreteHandlerA();
        AbstractHandler handlerB = new ConcreteHandlerB();
        AbstractHandler handlerZ = new ConcreteHandlerZ();
        // 如A處理不掉轉交給B
        handlerA.setHandler(handlerB);
        handlerB.setHandler(handlerZ);
        handlerA.handleRequest("Z");
    }

}

4.運行效果微信

----------------------handleRequest("A")-------------------------
ConcreteHandlerA處理
----------------------handleRequest("B")-------------------------
ConcreteHandlerA不處理,由其餘的Handler處理
ConcreteHandlerB處理
----------------------handleRequest("Z")-------------------------
ConcreteHandlerA不處理,由其餘的Handler處理
ConcreteHandlerB不處理,由其餘的Handler處理
ConcreteHandlerZ處理

能夠看出,客戶端建立了三個處理者對象,並指定第一個處理者對象的下家是第二個處理者對象,第二個處理者對象的下家是第三個處理者對象。而後客戶端將請求傳遞給第一個處理者對象。

因爲本示例的傳遞邏輯很是簡單:只要有下家,就傳給下家處理;若是沒有下家,就自行處理。所以,第一個處理者對象接到請求後,會將請求傳遞給第二個處理者對象。因爲第二個處理者對象沒有下家,因而自行處理請求。活動時序圖以下所示。

時序圖

純與不純

  • 純:要麼承擔所有責任,要麼將責任推給下家,不容許出現某一個具體處理者對象在承擔了一部分或所有責任後又將責任向下傳遞的狀況。
  • 不純:容許某個請求被一個具體處理者部分處理後再向下傳遞,或者一個具體處理者處理完某請求後其後繼處理者能夠繼續處理該請求,並且一個請求能夠最終不被任何處理者對象所接收。

Filter源碼分析

咱們常用的Filter就使用到了責任鏈模式,建立一個Filter除了要在應用中作相應配置外,還須要實現javax.servlet.Filter接口。

@Configuration
public class MyFilter implements Filter {

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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        LOGGER.info("init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        LOGGER.info("doFilter");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        LOGGER.info("destroy");
    }
}

使用DEBUG模式所看到的結果以下:

Filter應用

Filter

在源碼ApplicationFilterChain中,定義了一個ApplicationFilterConfig的數組來存放全部的Filter,使之造成鏈狀

public final class ApplicationFilterChain implements FilterChain {
    // 擴容規則
    public static final int INCREMENT = 10;
    // Filter集,默認大小爲 0 個  
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
}

ApplicationFilterConfig 裝載規則

在應用首次啓動時,會自動實例化對象,並從web應用中讀取配置的Filter的信息,而後裝進該容器。

public final class ApplicationFilterConfig implements FilterConfig, Serializable {
    // 省略代碼...
}

ApplicationFilterConfig 擴容規則

//將過濾器添加到在此鏈條中執行的過濾器集合中。
void addFilter(ApplicationFilterConfig filterConfig) {

    // 防止屢次添加相同的過濾器
    for(ApplicationFilterConfig filter:filters)
        if(filter==filterConfig)
            return;

    if (n == filters.length) {
        // 定義一個在原基礎之上 +10 的新數組
        ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];
        // 將源數組的元素拷貝到目標數組中去
        System.arraycopy(filters, 0, newFilters, 0, n);
        // 從新賦值
        filters = newFilters;
    }
    //將變量filterConfig放入ApplicationFilterConfig數組中,並將當前過濾器鏈裏面擁有的過濾器數目+1
    filters[n++] = filterConfig;
}

addFilter 的使用

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

        //若是沒有 servlet 要執行,則返回null
        if (servlet == null)
            return null;

        // 建立和初始化過濾器鏈對象
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // 爲了安全起見:不要回收
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // 調度程序在使用中
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // 獲取過濾器上下文映射
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // 若是沒有過濾器映射,就認爲當前執行完成
        if ((filterMaps == null) || (filterMaps.length == 0))
            return (filterChain);

        // 獲取匹配的過濾器映射信息
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // 將相關路徑映射的過濾器添加到此過濾器鏈中
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 添加與servlet名稱匹配的篩選器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 返回完整的過濾器鏈
        return filterChain;
    }

createFilterChain 的調用

final class StandardWrapperValve extends ValveBase {
    public final void invoke(Request request, Response response) throws IOException, ServletException {
        // 省略代碼...
        // 爲這個請求建立過濾器鏈
        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
        // 省略代碼...
        filterChain.doFilter(request.getRequest(), response.getResponse());
    }
}

執行流程

在StandardWrapperValue類的invoke()方法中調用ApplicationFilterChai類的createFilterChain()方法
在ApplicationFilterChai類的createFilterChain()方法中調用ApplicationFilterChain類的addFilter()方法
在ApplicationFilterChain類的addFilter()方法中給ApplicationFilterConfig數組賦值。

執行流程

最後一步

doFilter()方法中最後會調用一個internalDoFilter()方法,目的就是執行ApplicationFilterChain中的所有過濾器,從代碼中能夠發現它調用了doFilter,而在doFilter又會調用internalDoFilter 從而使全部的Filter都得以調用

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

    // 若是存在下一個,繼續調用下一個過濾器
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            Filter filter = filterConfig.getFilter();

            if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                    filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
            }
            if( Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();

                Object[] args = new Object[]{req, res, this};
                SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
            } else {
                // 此處調用Filter的doFilter()方法  / 而 doFilter 又會調用 internalDoFilter 直到調用完全部的過濾器
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.filter"), e);
        }
        return;
    }

    // 從最後一個開始調用
    try {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(request);
            lastServicedResponse.set(response);
        }

        if (request.isAsyncSupported() && !servletSupportsAsync) {
            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                    Boolean.FALSE);
        }
        // 包裝請求
        if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse) &&
                Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            Principal principal =
                ((HttpServletRequest) req).getUserPrincipal();
            Object[] args = new Object[]{req, res};
            SecurityUtil.doAsPrivilege("service",
                                       servlet,
                                       classTypeUsedInService,
                                       args,
                                       principal);
        } else {
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        throw e;
    } catch (Throwable e) {
        e = ExceptionUtils.unwrapInvocationTargetException(e);
        ExceptionUtils.handleThrowable(e);
        throw new ServletException(sm.getString("filterChain.servlet"), e);
    } finally {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(null);
            lastServicedResponse.set(null);
        }
    }
}

總結

職責鏈模式經過創建一條鏈來組織請求的處理者,請求將沿着鏈進行傳遞,請求發送者無須知道請求在什麼時候、何處以及如何被處理,實現了請求發送者與處理者的解耦。在軟件開發中,若是遇到有多個對象能夠處理同一請求時能夠應用職責鏈模式,例如在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

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659

微信公衆號:battcn(歡迎調戲)

福利

關注公衆號:battcn,回覆springboot便可得到 <Spring Boot從入門到實戰 基礎實戰系列教程全集><2017最新spring boot 外賣實戰微信公衆平臺視頻教程>

相關文章
相關標籤/搜索