責任鏈模式之——Pipeline-Valve 管道(Tomcat)分析及仿寫


1. Pipeline-Valve 管道(Tomcat)簡介

責任鏈模式是一種對象的行為模式。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。

Tomcat 中的 Pipeline-Valve 實際上就是責任鏈模式,責任鏈模式是指在一個請求處理的過程中有多個處理者依次對請求進行處理,每個處理者負責做自己相應的處理,處理完成后將處理后的請求返回,再讓下一個處理者繼續處理,就好像駕車的過程中可能會遇到很多次交警檢查,可能有查酒駕的也可能有查違章的,在一次駕車的過程中可能會遇到多次檢查,這就是責任鏈模式,Pipeline 就相當於駕車的過程, Valve 相當於檢查的交警。 

不過Pipeline-Valve 的管道模型和普通的責任鏈模式稍微有點不同,區別主要有兩點:

  • 每個Pipeline 都有特定的Value ,而且是在管道的最后一個執行,這個Valve 叫BaseValve,BaseValve 是不可刪除的;
  • 在上層容器的管道的BaseValue 中會調用下層容器的管道。4 個容器的BaseValve 分別是StandardEngineValve 、StandardHostValve 、StandardContextValve 和StandardWrapperValve,整個處理的流程如下圖: 

下面代碼即為實現了Value接口的基礎類ValveBase,它有一個Valve類型的內部屬性next,即同一個Pipeline中的后續Valve的引用。類似於單向鏈表。繼承這個ValveBase針對不同的容器實現了不同版本的閥如StandardEngineValve,StandardHostValve,StandardContextValve,StandardWrapperValve等。他們之間不同的實現就是invoke和event的方法不同。而實際上也就是請求的路由選擇,Filter應用和Servlet的處理。

public abstract class ValveBase extends LifecycleMBeanBase
    implements Contained, Valve
{
    /**
     * The next Valve in the pipeline this Valve is a component of.
     */
    protected Valve next = null;
 
    @Override
    public Container getContainer() {
 
        return (container);
 
    }
 
    @Override
    public void setContainer(Container container) {
 
        this.container = container;
 
    }
 
    @Override
    public Valve getNext()
    {
        return (next);
    }
 
    @Override
    public void setNext(Valve valve)
    {
        this.next = valve;
    }
 
    @Override
    public abstract void invoke(Request request, Response response)
        throws IOException, ServletException;
 
    @Override
    public void event(Request request, Response response, CometEvent event)
        throws IOException, ServletException {
        // Perform the request
        getNext().event(request, response, event);
    }
}

2. Pipeline-Valve責任鏈設計

 實現寫博客需求,設計如下:

Pipeline 為一條管道,Valve是一個個閥門,BasicValve是最后一個閥門,業務從第一個閥門進入,依次流經每個閥門進行業務處理,之后依次返回。

代碼如下:

2.1 Pipeline管道設計:

定義一個Pipeline接口:

import com.zang.pipelinevalve.valve.Valve;

public interface Pipeline {
    /**
     * 后置閥門設置
     * @param basic
     */
    void setBasic(Valve basic);

    /**
     * 設置后面閥門
     * @param valve
     */
    void addValve(Valve valve);

    /**
     * pipeline的執行方法
     * @param i
     */
    void invoke(int i);
}

創建寫博客Pipeline管道實現

import com.zang.pipelinevalve.pipeline.Pipeline;
import com.zang.pipelinevalve.valve.Valve;

/**
 * 寫博客責任鏈管道
 */
public class BloggingPipeline implements Pipeline {

    /**
     * 第一個閥門
     */
    protected Valve first = null;
    /**
     * 最后一個閥門
     */
    protected Valve basic = null;


    @Override
    public void setBasic(Valve valve) {
        Valve oldBasic = this.basic;
        Valve current = first;
        while (current != null) {
            if (current.getNext() == oldBasic) {
                current.setNext(valve);
                break;
            }
            current = current.getNext();
        }

        this.basic = valve;
    }

    public Valve getFirst() {
        if (first != null) {
            return first;
        }

        return basic;
    }

    @Override
    public void addValve(Valve valve) {
        if (first == null) {
            first = valve;
            valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
                if (current.getNext() == basic) {
                    current.setNext(valve);
                    valve.setNext(basic);
                    break;
                }
                current = current.getNext();
            }
        }
    }

    @Override
    public void invoke(int i) {
        Valve valve = getFirst();
        if (valve!=null) {
            valve.invoke(i);
            return;
        }
        throw new RuntimeException("責任鏈上沒有閥門");
    }

}

2.2 Valve閥門設計

定義一個Valve接口:

public interface Valve {
    void invoke(int i);

    void setNext(Valve valve);

    Valve getNext();
}

有多個閥門實現,我們將一些通用的變量方法放入 基礎閥門 中。

public abstract class BaseValve implements Valve{
    /**
     * 下一個閥門
     */
    protected Valve next = null;

    @Override
    public void setNext(Valve valve) {
        this.next = valve;
    }

    @Override
    public Valve getNext() {
        return next;
    }
}

具體閥門類

public class ComputerOperationValve extends BaseValve{

    @Override
    public void invoke(int i) {
        System.out.println("開機:"+i++);
        this.next.invoke(i);
        System.out.println("關機:"+--i);
    }
}

public class BrowserOperationValve extends BaseValve{

    @Override
    public void invoke(int i) {
        System.out.println("打開瀏覽器:"+i++);
        this.next.invoke(i);
        System.out.println("關閉瀏覽器:"+ --i);
    }
}

public class WebsiteOperationValve extends BaseValve{

    @Override
    public void invoke(int i) {
        System.out.println("登錄網頁:"+i++);
        this.next.invoke(i);
        System.out.println("登出網頁:"+--i);
    }
}

public class BlogOperationValve extends BaseValve{

    @Override
    public void invoke(int i) {
        System.out.println("\n完成博客: "+i +"\n");
    }
}

2.3 測試

        //創建管道
        BloggingPipeline pipeline = new BloggingPipeline();
        //創建閥門
        ComputerOperationValve computerOperationValve = new ComputerOperationValve();
        BrowserOperationValve browserOperationValve = new BrowserOperationValve();
        WebsiteOperationValve websiteOperationValve = new WebsiteOperationValve();
        BlogOperationValve blogOperationValve = new BlogOperationValve();

        //管道執行
        pipeline.addValve(computerOperationValve);
        pipeline.addValve(browserOperationValve);
        pipeline.addValve(websiteOperationValve);
        pipeline.setBasic(blogOperationValve);
        pipeline.invoke(0);

結果:

代碼結構供參考:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM