欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java設計模式責任鏈模式原理案例詳解

 更新時間:2021年09月15日 15:40:06   作者:大忽悠愛忽悠  
一個事件需要經(jīng)過多個對象處理是一個挺常見的場景,譬如采購審批流程,請假流程,軟件開發(fā)中的異常處理流程,web請求處理流程等各種各樣的流程,可以考慮使用責任鏈模式來實現(xiàn)

引言

以請假流程為例,一般公司普通員工的請假流程簡化如下:

在這里插入圖片描述

普通員工發(fā)起一個請假申請,當請假天數(shù)小于3天時只需要得到主管批準即可;當請假天數(shù)大于3天時,主管批準后還需要提交給經(jīng)理審批,經(jīng)理審批通過,若請假天數(shù)大于7天還需要進一步提交給總經(jīng)理審批。

使用 if-else 來實現(xiàn)這個請假流程的簡化代碼如下:

public class LeaveApproval
{
    public boolean process(String request, int number) {
        boolean result = handleByDirector(request); // 主管處理
        if (result == false) {  // 主管不批準
            return false;
        } else if (number < 3) {    // 主管批準且天數(shù)小于 3
            return true;
        }
        result = handleByManager(request); // 準管批準且天數(shù)大于等于 3,提交給經(jīng)理處理
        if (result == false) {   // 經(jīng)理不批準
            return false;
        } else if (number < 7) { // 經(jīng)理批準且天數(shù)小于 7
            return true;
        }
        result = handleByTopManager(request);   // 經(jīng)理批準且天數(shù)大于等于 7,提交給總經(jīng)理處理
        if (result == false) { // 總經(jīng)理不批準
            return false;
        }
        return true;    // 總經(jīng)理最后批準
    }
    private boolean handleByDirector(String request)
    {
        // 主管處理該請假申請
        if(request.length()>10)
            return false;
        return true;
    }
    private boolean handleByManager(String request) {
        // 經(jīng)理處理該請假申請
        if(request.length()>5)
        return false;
        return true;
    }
    private boolean handleByTopManager(String request) {
        // 總經(jīng)理處理該請假申請
        if(request.length()>3)
            return false;
        return true;
    }
}

問題看起來很簡單,三下五除二就搞定,但是該方案存在幾個問題:

  •  LeaveApproval 類比較龐大,各個上級的審批方法都集中在該類中,違反了 “單一職責原則”,測試和維護難度大
  •  當需要修改該請假流程,譬如增加當天數(shù)大于30天時還需提交給董事長處理,必須修改該類源代碼(并重新進行嚴格地測試),違反了"開閉原則"
  • 該流程缺乏靈活性,流程確定后不可再修改(除非修改源代碼),客戶端無法定制流程

使用責任鏈模式可以解決上述問題。

責任鏈模式定義

避免請求發(fā)送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有對象處理它為止。職責鏈模式是一種對象行為型模式。

責任鏈可以是一條直線、一個環(huán)或者一個樹形結(jié)構(gòu),最常見的職責鏈是直線型,即沿著一條單向的鏈來傳遞請求,如下圖所示。鏈上的每一個對象都是請求處理者,責任鏈模式可以將請求的處理者組織成一條鏈,并讓請求沿著鏈傳遞,由鏈上的處理者對請求進行相應的處理。在此過程中,客戶端實際上無須關(guān)心請求的處理細節(jié)以及請求的傳遞,只需將請求發(fā)送到鏈上即可,從而實現(xiàn)請求發(fā)送者和請求處理者解耦。

在這里插入圖片描述

對責任鏈的理解,關(guān)鍵在于對鏈的理解,即包含如下兩點:

  •  鏈是一系列節(jié)點的集合,在責任鏈中,節(jié)點實質(zhì)上是指請求的處理者;
  • 鏈的各節(jié)點可靈活拆分再重組,在責任鏈中,實質(zhì)上就是請求發(fā)送者與請求處理者的解耦。

類圖

在這里插入圖片描述

角色

我們可以從責任鏈模式的結(jié)構(gòu)圖中看到,具體的請求處理者可以有多個,并且所有的請求處理者均具有相同的接口(繼承于同一抽象類)。 責任鏈模式主要包含如下兩個角色

 Handler(抽象處理者):處理請求的接口,一般設計為具有抽象請求處理方法的抽象類,以便于不同的具體處理者進行繼承,從而實現(xiàn)具體的請求處理方法。此外,由于每一個請求處理者的下家還是一個處理者,因此抽象處理者本身還包含了一個本身的引用( successor)作為其對下家的引用,以便將處理者鏈成一條鏈;

 ConcreteHandler(具體處理者):它是抽象處理者的子類,可以處理用戶請求,在具體處理者類中實現(xiàn)了抽象處理者中定義的抽象請求處理方法,在處理請求之前需要進行判斷,看是否有相應的處理權(quán)限,如果可以處理請求就處理它,否則將請求轉(zhuǎn)發(fā)給后繼者;在具體處理者中可以訪問鏈中下一個對象,以便請求的轉(zhuǎn)發(fā)。

在責任鏈模式里,由每一個請求處理者對象對其下家的引用而連接起來形成一條請求處理鏈。請求將在這條鏈上一直傳遞,直到鏈上的某一個請求處理者能夠處理此請求。事實上,發(fā)出這個請求的客戶端并不知道鏈上的哪一個請求處理者將處理這個請求,這使得系統(tǒng)可以在不影響客戶端的情況下動態(tài)地重新組織鏈和分配責任。

核心

實現(xiàn)責任鏈模式的關(guān)鍵核心是: 在抽象類 Handler 里面聚合它自己(持有自身類型的引用),并在 handleRequest 方法里判斷其是否能夠處理請求。若當前處理者無法處理,則設置其后繼者并向下傳遞,直至請求被處理。

示例代碼

1、對請求處理者的抽象

責任鏈模式的核心在于對請求處理者的抽象。在實現(xiàn)過程中,抽象處理者一般會被設定為抽象類

其典型實現(xiàn)代碼如下所示:

public abstract class Handler {
    // protected :維持對下家的引用
    protected Handler successor;
    public void setSuccessor(Handler successor) {
        this.successor=successor;
    }
    public abstract void handleRequest(String request);
}

上述代碼中,抽象處理者類定義了對下家的引用 (其一般用 protected 進行修飾),以便將請求轉(zhuǎn)發(fā)給下家,從而形成一條請求處理鏈。同時,在抽象處理者類中還聲明了抽象的請求處理方法,以便由子類進行具體實現(xiàn)。

2、對請求處理者的抽象

具體處理者是抽象處理者的子類,具體處理者類的典型代碼如下:

public class ConcreteHandler extends Handler {
    public void handleRequest(String request) {
        if (請求滿足條件) {
            //處理請求
        }else {
            this.successor.handleRequest(request);  //轉(zhuǎn)發(fā)請求
        }
    }
}

在具體處理類中,通過對請求進行判斷以便做出相應的處理,因此,其一般具有兩大作用:

  • 處理請求,不同的具體處理者以不同的形式實現(xiàn)抽象請求處理方法 handleRequest();
  • 轉(zhuǎn)發(fā)請求,若該請求超出了當前處理者類的權(quán)限,可以將該請求轉(zhuǎn)發(fā)給下家;

3、責任鏈的創(chuàng)建

需要注意的是,責任鏈模式并不創(chuàng)建職責鏈,職責鏈的創(chuàng)建工作必須由系統(tǒng)的其他部分來完成,一般由使用該責任鏈的客戶端創(chuàng)建。職責鏈模式降低了請求的發(fā)送者和請求處理者之間的耦合,從而使得多個請求處理者都有機會處理這個請求。

責任鏈實現(xiàn)請假案例

請假信息類,包含請假人姓名和請假天數(shù)

@Data
@AllArgsConstructor
public class LeaveRequest
{
 String name;//請假人的姓名
 Integer num;//請假天數(shù)
}

抽象處理者類 Handler,維護一個 nextHandler 屬性,該屬性為當前處理者的下一個處理者的引用;聲明了抽象方法 process

//抽象處理者
@Data
public abstract class Handler
{
    //維護自身引用
    protected Handler handler;
   //當前處理者的姓名
    protected String name;
    //傳入當前處理者的姓名
    public Handler(String name)
    {
        this.name=name;
    }
    //抽象方法,用來處理請假的請求
    public abstract Boolean process(LeaveRequest leaveRequest);
}

三個具體處理類,分別實現(xiàn)了抽象處理類的 process 方法

主管:

public class Director extends Handler{
    public Director(String name) {
        super(name);
    }
    //處理請假的請求
    @Override
    public Boolean process(LeaveRequest leaveRequest) {
       //隨機數(shù)大于3,就批準請求
        boolean result = (new Random().nextInt(10)) > 3;
        String log = "主管: %s,審批:%s的請假申請,請假天數(shù):%d,審批結(jié)果:%s";
        System.out.println(String.format(log,name,leaveRequest.getName(),leaveRequest.getNum(),result==true?"通過":"不通過"));
        if(result)//批準
        {
            //如果請假天數(shù),超過了3天,那么交給上級繼續(xù)審批
           if(leaveRequest.num>3)
           {
               return nextHandler.process(leaveRequest);
           }
           //請假天數(shù)小于3,審批通過
            return true;
        }
        //沒有通過審批
        return false;
    }
}

經(jīng)理

public class Manager extends Handler{
    public Manager(String name) {
        super(name);
    }
    //處理請假的請求
    @Override
    public Boolean process(LeaveRequest leaveRequest) {
        boolean result = (new Random().nextInt(10)) > 3; // 隨機數(shù)大于3則為批準,否則不批準
        String log = "經(jīng)理: %s,審批:%s的請假申請,請假天數(shù):%d,審批結(jié)果:%s";
        System.out.println(String.format(log,name,leaveRequest.getName(),leaveRequest.getNum(),result==true?"批準":"不通過"));
        if(result)
        {
            //請假天數(shù)過多,還是需要提交到更高的一級去審批
            if(leaveRequest.getNum()>7)
            {
                return nextHandler.process(leaveRequest);
            }
            //否則直接通過
            return true;
        }
        return false;
    }
}

總經(jīng)理

public class TopManager extends Handler{
    public TopManager(String name) {
        super(name);
    }
    @Override
    public Boolean process(LeaveRequest leaveRequest) {
        //隨機數(shù)大于3,就批準請求
        boolean result = (new Random().nextInt(10)) > 3;
        String log = "總經(jīng)理: %s,審批:%s的請假申請,請假天數(shù):%d,審批結(jié)果:%s";
        System.out.println(String.format(log,name,leaveRequest.getName(),leaveRequest.getNum(),result==true?"通過":"不通過"));
        if(result)//批準
        {
            //默認只有三個處理器,但是如果后續(xù)還要加,也需要留個位置
            //如果后續(xù)繼續(xù)添加
            if(nextHandler!=null)
            {
                return nextHandler.process(leaveRequest);
            }
            return true;
        }
        //沒有通過審批
        return false;
    }
}

處理器鏈類:

//處理器鏈
public class HandlerChain
{
    //維護第一個處理器
    private Handler director=new Director("小忽悠");
    //默認有三個處理器
    public HandlerChain()
    {
        //默認有三個處理器鏈
        //并且這三個處理器有先后關(guān)系
        director.nextHandler=new Manager("小朋友");
        director.nextHandler.nextHandler=new TopManager("超級大忽悠");
    }
    //添加一個處理器進集合
    public void addHandler(Handler handler)
    {
        Handler temp=director;
     while(temp.nextHandler!=null)
     {
         temp=temp.nextHandler;
     }
        temp.nextHandler=handler;
    }
    //執(zhí)行處理器鏈
     public void process(LeaveRequest leaveRequest)
    {
        //第一個處理器,如果可以處理器就不需要交給下一個處理器處理了
        //否則,繼續(xù)交給下一個處理器處理
        director.process(leaveRequest);
    }
}

客戶端測試:

public class Client
{
    public static void main(String[] args) {
        LeaveRequest leaveRequest=new LeaveRequest("大忽悠",10);
        HandlerChain handlerChain=new HandlerChain();
        handlerChain.process(leaveRequest);
    }
}

在這里插入圖片描述

案例類圖

在這里插入圖片描述

與上面所給出的類圖不同的是,我通過一個處理器鏈類,把調(diào)用處理器鏈處理業(yè)務邏輯和客戶端分離開來,進一步解耦

可擴展性

如果此時審批流程還需要加上一步,就非常方便

例如,我們需要增加一個上帝,來對請假流程做最終的處理,那么我們只需要創(chuàng)建一個上帝處理器實現(xiàn)處理器抽象類,然后添加進處理器鏈中即可

public class God extends Handler{
    public God(String name) {
        super(name);
    }

    @Override
    public Boolean process(LeaveRequest leaveRequest) {
        System.out.println("上帝保佑你,所以你可以放假了");
        return true;
    }
}

客戶端:

public class Client
{
    public static void main(String[] args) {
        LeaveRequest leaveRequest=new LeaveRequest("大忽悠",10);
        HandlerChain handlerChain=new HandlerChain();
        handlerChain.addHandler(new God("上帝"));
        handlerChain.process(leaveRequest);
    }
}

在這里插入圖片描述

如果還想繼續(xù)添加處理器,就需要在上帝process方法中預留一個接口

這樣很麻煩,我這里沒有繼續(xù)對方法抽取,進行解耦,感興趣的小伙伴,可以繼續(xù)嘗試解耦

純與不純的責任鏈模式

純的責任鏈模式

  • 一個具體處理者對象只能在兩個行為中選擇一個:要么承擔全部責任,要么將責任推給下家,不允許出現(xiàn)某一個具體處理者對象在承擔了一部分或全部責任后又將責任向下傳遞的情況
  • 一個請求必須被某一個處理者對象所接收,不能出現(xiàn)某個請求未被任何一個處理者對象處理的情況

不純的責任鏈模式

  • 允許某個請求被一個具體處理者部分處理后再向下傳遞
  • 或者一個具體處理者處理完某請求后其后繼處理者可以繼續(xù)處理該請求
  •  而且一個請求可以最終不被任何處理者對象所接收

責任鏈模式主要優(yōu)點

  • 對象僅需知道該請求會被處理即可,且鏈中的對象不需要知道鏈的結(jié)構(gòu),由客戶端負責鏈的創(chuàng)建,降低了系統(tǒng)的耦合度
  • 請求處理對象僅需維持一個指向其后繼者的引用,而不需要維持它對所有的候選處理者的引用,可簡化對象的相互連接
  • 在給對象分派職責時,職責鏈可以給我們更多的靈活性,可以在運行時對該鏈進行動態(tài)的增刪改,改變處理一個請求的職責
  • 新增一個新的具體請求處理者時無須修改原有代碼,只需要在客戶端重新建鏈即可,符合 “開閉原則”

職責鏈模式的主要缺點

  • 一個請求可能因職責鏈沒有被正確配置而得不到處理
  • 對于比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統(tǒng)性能將受到一定影響,且不方便調(diào)試
  • 可能因為職責鏈創(chuàng)建不當,造成循環(huán)調(diào)用,導致系統(tǒng)陷入死循環(huán)

適用場景

  •  有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時刻再確定,客戶端只需將請求提交到鏈上,而無須關(guān)心請求的處理對象是誰以及它是如何處理的
  •  在不明確指定接收者的情況下,向多個對象中的一個提交一個請求
  •  可動態(tài)指定一組對象處理請求,客戶端可以動態(tài)創(chuàng)建職責鏈來處理請求,還可以改變鏈中處理者之間的先后次序

模擬實現(xiàn)Tomcat中的過濾器機制

第一步:定義封裝請求的類Request和封裝處理結(jié)果響應的類Response

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Reponse
{
    private List<String> data=new ArrayList<>();
    public void addData(String data)
    {
        this.data.add(data);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Request
{
   private Object data;
}

第二步:定義具有過濾功能的接口Filter,具體的過濾規(guī)則需要實現(xiàn)該接口

/*
 * 定義接口Filter,具體的過濾規(guī)則需要實現(xiàn)這個接口,最后一個參數(shù)添加的意義是我們在Main函數(shù)中:
 * fc.doFilter(request, response,fc);執(zhí)行這一步的時候可以按照規(guī)則鏈條一次使用三個過濾規(guī)則對字符串進行處理
 */
public interface Filter
{
    void doFilter(Request request,Reponse reponse,FilterChain filterChain);
}

第三步:定義具體的過濾處理規(guī)則

public class StuAgeFilter implements Filter
{
    @Override
    public void doFilter(Request request, Reponse reponse, FilterChain filterChain) {
        Stu stu = (Stu) request.getData();
        if(stu.getName().contains("忽悠"))
        {
            //名字不符合要求
            reponse.addData("名字不符合要求");
        }
        //名字符合要求
         reponse.addData("名字符合要求");
        filterChain.doFilter(request,reponse,filterChain);
    }
}
//學生過濾器--過濾出18歲以上的
public class StuFilter implements Filter
{
    @Override
    public void doFilter(Request request, Reponse reponse, FilterChain filterChain) {
        Stu stu = (Stu)request.getData();
        if(stu.getAge()<18)
        {
            //不放行
            reponse.addData("年齡不符合要求");
        }
        //放行
        reponse.addData("年齡滿足要求");
        filterChain.doFilter(request,reponse,filterChain);
    }
}

第四步:定義責任鏈FilterChain

//過濾鏈條
@Data
public class FilterChain
{
    //用List集合來存過濾器
    private List<Filter> filters = new ArrayList<Filter>();
    //用于標記規(guī)則的引用順序
   private int index;
   public FilterChain()
   {
       //初始化為0
       index=0;
   }
    //往過濾器鏈條中添加新的過濾器
    public FilterChain addFilter(Filter f)
    {
        filters.add(f);
        //代碼的設計技巧:Chain鏈添加過濾規(guī)則結(jié)束后返回添加后的Chain,方便我們下面doFilter函數(shù)的操作
        return this;
    }
    public void doFilter(Request request, Reponse response, FilterChain chain){
        //index初始化為0,filters.size()為3,不會執(zhí)行return操作
        //說明所有過濾器都執(zhí)行完了
        if(index==filters.size()){
            return;
        }
        //獲取當前過濾器
        Filter f=filters.get(index);
        //下一次獲取的時候,就是下一個過濾器了
        index++;
        //執(zhí)行當前過濾器的過濾方法
        f.doFilter(request, response, chain);
   }
}

第五步:測試

public class Client
{
    public static void main(String[] args)
    {
      //創(chuàng)建請求對象
        Request request=new Request();
        request.setData(new Stu("小朋友",19));
        //創(chuàng)建響應對象
        Reponse reponse=new Reponse();
        //創(chuàng)建一個過濾器鏈
        FilterChain filterChain=new FilterChain();
        filterChain.addFilter(new StuAgeFilter());
        filterChain.addFilter(new StuFilter());
        //執(zhí)行
        filterChain.doFilter(request,reponse,filterChain);
        reponse.getData().forEach(x->{
            System.out.println(x);
        });
    }
}

在這里插入圖片描述

運行過程如下

在這里插入圖片描述

分析Tomcat 過濾器中的責任鏈模式

Servlet 過濾器是可用于 Servlet 編程的 Java 類,可以實現(xiàn)以下目的:在客戶端的請求訪問后端資源之前,攔截這些請求;在服務器的響應發(fā)送回客戶端之前,處理這些響應。

Servlet 定義了過濾器接口 Filter 和過濾器鏈接口 FilterChain 的源碼如下

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

我們自定義一個過濾器的步驟是:

1)寫一個過濾器類,實現(xiàn) javax.servlet.Filter 接口,如下所示

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        // 做一些自定義處理....
        System.out.println("執(zhí)行doFilter()方法之前...");
        chain.doFilter(request, response);              // 傳遞請求給下一個過濾器
        System.out.println("執(zhí)行doFilter()方法之后...");
    }
    @Override
    public void destroy() {
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

2)在 web.xml 文件中增加該過濾器的配置,譬如下面是攔截所有請求

<filter>  
        <filter-name>MyFilter</filter-name>  
        <filter-class>com.whirly.filter.MyFilter</filter-class>  
</filter>
<filter-mapping>  
        <filter-name>MyFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
</filter-mapping>

當啟動 Tomcat 是我們的過濾器就可以發(fā)揮作用了。那么過濾器是怎樣運行的呢?

TomcatPipeline Valve機制,也是使用了責任鏈模式,一個請求會在 Pipeline 中流轉(zhuǎn),Pipeline 會調(diào)用相應的 Valve 完成具體的邏輯處理;
其中的一個基礎ValveStandardWrapperValve,其中的一個作用是調(diào)用 ApplicationFilterFactory 生成 Filter鏈,具體代碼在 invoke 方法中

在運行過濾器之前需要完成過濾器的加載和初始化,以及根據(jù)配置信息生成過濾器鏈:

  • 過濾器的加載具體是在 ContextConfig 類的 configureContext 方法中,分別加載 filterfilterMap 的相關(guān)信息,并保存在上下文環(huán)境中
  • 過濾器的初始化在 StandardContext 類的 startInternal 方法中完成,保存在 filterConfigs中并存到上下文環(huán)境中
  • 請求流轉(zhuǎn)到 StandardWrapperValve 時,在 invoke 方法中,會根據(jù)過濾器映射配置信息,為每個請求創(chuàng)建對ApplicationFilterChain,其中包含了目標 Servlet 以及對應的過濾器鏈,并調(diào)用過濾器鏈的 doFilter 方法執(zhí)行過濾器

StandardWrapperValve 調(diào)用 ApplicationFilterFactory 為請求創(chuàng)建過濾器鏈并調(diào)用過濾器鏈的關(guān)鍵代碼如下:

final class StandardWrapperValve extends ValveBase {
    public final void invoke(Request request, Response response) throws IOException, ServletException {
        // 省略其他的邏輯處理...
        // 調(diào)用 ApplicationFilterChain.createFilterChain() 創(chuàng)建過濾器鏈
        ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
        if (servlet != null && filterChain != null) {
            // 省略
        } else if (request.isAsyncDispatching()) {
            request.getAsyncContextInternal().doInternalDispatch();
        } else if (comet) {
            filterChain.doFilterEvent(request.getEvent());
        } else {
            // 調(diào)用過濾器鏈的 doFilter 方法開始過濾
            filterChain.doFilter(request.getRequest(), response.getResponse());
        }

過濾器鏈 ApplicationFilterChain 的關(guān)鍵代碼如下,過濾器鏈實際是一個 ApplicationFilterConfig 數(shù)組

final class ApplicationFilterChain implements FilterChain, CometFilterChain {
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; // 過濾器鏈
    private Servlet servlet = null; // 目標
    // ...
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if( Globals.IS_SECURITY_ENABLED ) {
            // ...
        } else {
            internalDoFilter(request,response); // 調(diào)用 internalDoFilter 方法
        }
    }
    private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // Call the next filter if there is one
        if (pos < n) {
            // 從過濾器數(shù)組中取出當前過濾器配置,然后下標自增1
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            try {
                filter = filterConfig.getFilter();  // 從過濾器配置中取出該 過濾器對象
                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 {
                    // 調(diào)用過濾器的 doFilter,完成一個過濾器的過濾功能
                    filter.doFilter(request, response, this);
                }
            return;  // 這里很重要,不會重復執(zhí)行后面的  servlet.service(request, response)
        }
        // 執(zhí)行完過濾器鏈的所有過濾器之后,調(diào)用 Servlet 的 service 完成請求的處理
        if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
            if( Globals.IS_SECURITY_ENABLED ) {

            } else {
                servlet.service(request, response);
            }
        } else {
            servlet.service(request, response);
        }
    }
    // 省略...
}

過濾器

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("執(zhí)行doFilter()方法之前...");
        chain.doFilter(request, response);              // 傳遞請求給下一個過濾器
        System.out.println("執(zhí)行doFilter()方法之后...");
    }

當下標小于過濾器數(shù)組長度 n 時,說明過濾器鏈未執(zhí)行完,所以從數(shù)組中取出當前過濾器,調(diào)用過濾器的 doFilter 方法完成過濾處理,在過濾器的 doFilter 中又調(diào)用 FilterChaindoFilter,回到 ApplicationFilterChain,又繼續(xù)根據(jù)下標是否小于數(shù)組長度來判斷過濾器鏈是否已執(zhí)行完,未完則繼續(xù)從數(shù)組取出過濾器并調(diào)用 doFilter 方法,所以這里的過濾鏈是通過嵌套遞歸的方式來串成一條鏈。

當全部過濾器都執(zhí)行完畢,最后一次進入 ApplicationFilterChain.doFilter 方法的時候 pos < nfalse,不進入 if (pos < n) 中,而是執(zhí)行后面的代碼,判斷 (request instanceof HttpServletRequest) && (response instanceof HttpServletResponse),若為 http 請求則調(diào)用 servlet.service(request, response); 來處理該請求。

處理完畢之后沿著調(diào)用過濾器的順序反向退棧,分別執(zhí)行過濾器中 chain.doFilter() 之后的處理邏輯,需要注意的是在 if (pos < n) 方法體的最后有一個 return;,這樣就保證了只有最后一次進入 ApplicationFilterChain.doFilter 方法的調(diào)用能夠執(zhí)行后面的 servlet.service(request, response) 方法

畫一個簡要的調(diào)用棧如下所示:

在這里插入圖片描述

ApplicationFilterChain 類扮演了抽象處理者角色,具體處理者角色由各個 Filter 扮演

以上就是java設計模式責任鏈模式原理案例詳解的詳細內(nèi)容,更多關(guān)于java設計模式責任鏈模式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java web支持jsonp的實現(xiàn)代碼

    java web支持jsonp的實現(xiàn)代碼

    這篇文章主要介紹了java web支持jsonp的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-11-11
  • Feign如何使用protobuf的類作為參數(shù)調(diào)用

    Feign如何使用protobuf的類作為參數(shù)調(diào)用

    這篇文章主要介紹了Feign如何使用protobuf的類作為參數(shù)調(diào)用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot Logback日志記錄到數(shù)據(jù)庫的實現(xiàn)方法

    SpringBoot Logback日志記錄到數(shù)據(jù)庫的實現(xiàn)方法

    這篇文章主要介紹了SpringBoot Logback日志記錄到數(shù)據(jù)庫的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-11-11
  • SpringMVC Validator驗證示例

    SpringMVC Validator驗證示例

    SpringMVC服務器驗證一種是有兩種方式,一種是基于Validator接口,一種是使用Annotaion JSR-303標準的驗證,本篇文章主要介紹,有興趣的可以了解一下。
    2017-01-01
  • 深入了解Java線程池:從設計思想到源碼解讀

    深入了解Java線程池:從設計思想到源碼解讀

    這篇文章將從設計思想到源碼解讀,帶大家深入了解Java的線程池,文中的示例代碼講解詳細,對我們的學習或工作有一定的幫助,需要的可以參考一下
    2021-12-12
  • Java加載本地庫的方法之System.load與System.loadLibrary

    Java加載本地庫的方法之System.load與System.loadLibrary

    最近在做的工作要用到本地方法,所以下面這篇文章主要介紹了Java加載本地庫的方法之System.load與System.loadLibrary的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-09-09
  • java配置文件取值的多種方式總結(jié)

    java配置文件取值的多種方式總結(jié)

    這篇文章主要為大家詳細介紹了java配置文件取值的多種方式,包括一般項目,國際化項目,springboot項目,文中的示例代碼講解詳細,需要的可以參考下
    2023-11-11
  • SpringBoot Mybatis如何配置多數(shù)據(jù)源并分包

    SpringBoot Mybatis如何配置多數(shù)據(jù)源并分包

    這篇文章主要介紹了SpringBoot Mybatis如何配置多數(shù)據(jù)源并分包,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • Spring實戰(zhàn)之獲得Bean本身的id操作示例

    Spring實戰(zhàn)之獲得Bean本身的id操作示例

    這篇文章主要介紹了Spring實戰(zhàn)之獲得Bean本身的id操作,結(jié)合實例形式分析了spring獲取Bean本身id的相關(guān)配置與實現(xiàn)技巧,需要的朋友可以參考下
    2019-11-11
  • Spring Boot詳解配置文件有哪些作用與細則

    Spring Boot詳解配置文件有哪些作用與細則

    SpringBoot項目是一個標準的Maven項目,它的配置文件需要放在src/main/resources/下,其文件名必須為application,其存在兩種文件形式,分別是properties和yaml(或者yml)文件
    2022-07-07

最新評論