天天看點

深入Jetty源碼之ScopedHandlerc

在很多架構的設計中,都有類似channel鍊的設計,類似decorator模式或chain of responsibility模式,可以向這個channel注冊不同的handler,使用者請求可以穿越這個由多個handler組成的channel,執行響應的切面邏輯,在最後一個handler或者另一個processor處理用于自定義的業務邏輯,然後生成的響應可以逆着方向從這個channel回來。structs2中的interceptor、servlet中filter都采用這種設計。這種設計為面向切面程式設計提供了周遊,然而目前的handler設計更多的像是chain of responsibility模式,它的處理邏輯隻能從鍊頭走到鍊尾,而沒有傳回的路程。引入scopedhandler的目的就是用于解決這個問題。

structs2中interceptor實作使用傳入actioninvaction來調用channel中的後繼interceptor:

public class myinterceptor extends abstractinterceptor {

    @override

     public string intercept(actioninvocation ai) throws exception {

        try {

             // add user customized logic here when request come into the channel

             return ai.invoke();

        } finally {

             // add user customized logic here when response pass out across the channel

        }        

    } 

}

類似的,servlet中的filter實作也是使用filterchain來調用channel中後繼的filter:

public class myfilter implements filter {

    public void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception {

            // add user customized logic here when request come into the channel

            chain.dofilter(request, response);

            // add user customized logic here when response pass out across the channel

        }

    }

scopedhandler采用了不同的實作方式,首先它繼承自handlerwrapper,因而它使用handlerwrapper中的handler字段來建構handler鍊,然而并不是所有的handler都是scopedhandler,因而scopedhandler内部還定義了_nextscope字段用于建立在這條handler連結清單中的scopedhandler連結清單,以及_outerscope字段用于将自己始終和這個scopedhandler連結清單中的最外層scopedhandler相連,對這個scopedhandler的最外層清單,其_outscope字段為null。而scopedhandler要實作的行為是,假設a、b、c都是scopedhandler,并且它們組成連結清單:a->b->c,那麼當調用a.handle()方法時的調用堆棧是:

a.handle()

|-a.doscope()

|--b.doscope()

|----c.doscope()

|-----a.dohandle()

|------b.dohandle()

|-------c.dohandle()

而如果a、b是scopedhandler,x、y是其他的handler,并且它們組成連結清單:a->x->b->y,那麼當調用a.handle()方法時的調用棧是:

|---a.dohandle()

|----x.handle()

|-----b.dohandle()

|------y.handle()

這種行為主要用于servlet架構的實作,它可以保證在doscope()方法中做一些初始化工作,并且配置環境,而在後繼調用中都可以使用這些配置好的環境,并且dohandle()的順序還是使用原來定義的handler連結清單順序,即使有些handler并不是scopedhandler。

在實作中,其handler連結清單由handlerwrapper建構,在dostart()方法中計算_nextscope字段以及_outerscope字段,在handle()方法中,如果_outerscope為null,則調用doscope()方法,否則調用dohandle()方法:

    @override

    public final void handle(string target, request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception

    {

        if (_outerscope==null)  

            doscope(target,baserequest,request, response);

        else 

            dohandle(target,baserequest,request, response);

在執行完doscope()方法後,調用nextscope()方法,該方法順着_nextscope連結清單走,直到盡頭,後調用dohandle()方法:

    public final void nextscope(string target, request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception

        if (_nextscope!=null)

            _nextscope.doscope(target,baserequest,request, response);

        else if (_outerscope!=null)

            _outerscope.dohandle(target,baserequest,request, response);

而dohandle()方法在完成是調用nexthandle()方法,它也沿着_nextscope連結清單走,隻要_nextscope和_handler相同,則調用其dohandle()方法,但是如果_nextscope和_handler不同,則調用_handler中的handle()方法,用于處理在scopedhandler連結清單中插入非scopedhandler的情況:

    public final void nexthandle(string target, final request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception

        if (_nextscope!=null && _nextscope==_handler)

            _nextscope.dohandle(target,baserequest,request, response);

        else if (_handler!=null)

            _handler.handle(target,baserequest, request, response);

在scopedhandler的測試用例中給出了一個非常好的例子。首先有一個testhandler繼承自scopedhandler:

    private class testhandler extends scopedhandler {

        private final string _name;

        private testhandler(string name) {

            _name=name;

        @override

        public void doscope(string target, request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception {

            try {

                _history.append(">s").append(_name);

                super.nextscope(target,baserequest,request, response);

            } finally {

                _history.append("<s").append(_name);

            }

        public void dohandle(string target, request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception {

                _history.append(">w").append(_name);

                super.nexthandle(target,baserequest,request,response);

                _history.append("<w").append(_name);

然後有非scopedhandler的實作:

    private class otherhandler extends handlerwrapper {

        private otherhandler(string name) {

        public void handle(string target, request baserequest, httpservletrequest request, httpservletresponse response) throws ioexception, servletexception {

                _history.append(">h").append(_name);

                super.handle(target,baserequest,request, response);

                _history.append("<h").append(_name);

檢視一下test case的執行結果:

    @test

    public void testdouble() throws exception

        testhandler handler0 = new testhandler("0");

        otherhandler handlera = new otherhandler("a");

        testhandler handler1 = new testhandler("1");

        otherhandler handlerb = new otherhandler("b");

        handler0.sethandler(handlera);

        handlera.sethandler(handler1);

        handler1.sethandler(handlerb);

        handler0.start();

        handler0.handle("target",null,null,null);

        handler0.stop();

        string history=_history.tostring();

        system.err.println(history);

        assertequals(">s0>s1>w0>ha>w1>hb<hb<w1<ha<w0<s1<s0",history);

繼續閱讀