Jetty源码分析之ScopedHandler及Handler链

来源:互联网 发布:好看的输入法软件 编辑:程序博客网 时间:2024/05/22 06:05

ScopedHandler在Jetty的Handler体系中属于比较重要的一个成员,像ContextHandler、SessionHandler、ServletHandler、WebappContext等重要组件都直接或间接的继承了ScopedHandler,所以在学习其它更具体handler之前,先来分析下它的源码。先来看下ScopedHandler的类图:
这里写图片描述

从类图中可以看到继承的层次比较深,所以还是从父类开始分析,LifeCycle接口和AbstractHandler前面已经分析过,就不再次分析了。

1.HandlerContainer和AbstractHandlerContainer

HandlerContainer是一个接口,它的定义是一个包含其它Handlers的Handler。也就是本身是一个handler但又可以看成是可以持有其它handler的Handler容器,接口的源码比较简单,四个方法的含义都比较容易理解:

/** * A Handler that contains other Handlers. * <p> * The contained handlers may be one (see @{link {@link org.eclipse.jetty.server.handler.HandlerWrapper}) * or many (see {@link org.eclipse.jetty.server.handler.HandlerList} or {@link org.eclipse.jetty.server.handler.HandlerCollection}. * */public interface HandlerContainer extends LifeCycle{    //以数组形式返回含有的handlers    public Handler[] getHandlers();    //以数组形式返回含有的handler以及这些handler的子Handler    public Handler[] getChildHandlers();    //以数组形式返回ChildHandlers中类型为byclass类型的Handler    public Handler[] getChildHandlersByClass(Class<?> byclass);    //返回ChildHandlers中第一个类型为byclass的Handler    public <T extends Handler> T getChildHandlerByClass(Class<T> byclass);}

从类图上就可以看到AbstractHandlerContainer实现了HandleContainer并且继承了AbstractHandler,上面说到HandlerContainer的定义是既是一个Handler又是Handler容器,但因为它没有实现Handler接口所以并不能算是一个Handler,所以只有AbstractHandlerContainer才完全符合上面的定义,它事实上也是所有HandlerContainer子类的父类。
AbstractHandlerContainer实现了HandlerContainer接口中的后三个方法,getHandlers()方法留给了子类去实现。源码如下:

public Handler[] getChildHandlers()    {        Object list = expandChildren(null,null);        return (Handler[])LazyList.toArray(list, Handler.class);    }    /* ------------------------------------------------------------ */    public Handler[] getChildHandlersByClass(Class<?> byclass)    {        Object list = expandChildren(null,byclass);        return (Handler[])LazyList.toArray(list, byclass);    }    /* ------------------------------------------------------------ */    public <T extends Handler> T getChildHandlerByClass(Class<T> byclass)    {        // TODO this can be more efficient?        Object list = expandChildren(null,byclass);        if (list==null)            return null;        return (T)LazyList.get(list, 0);    }

可以看到都是借助expandChildren()方法来实现的,expandChildren()方法的目的是获取当前handler持有的所有handler对象及这些对象持有的子handler对象,将其加入到传入的list中去,如果传入了byClass参数则只选择那些和byClass类型相符的handler加入。但是这里其实还没有提供一个可以直接使用的实现,需要具体的子类去重写这个方法:

 protected Object expandChildren(Object list, Class<?> byClass)    {        return list;    }

另外还提供了一个expandHandler方法,来将制定Handler展开,并加入到传入的list中,这个方法在当前类中没有使用到,但是在子类中是用到了的。

protected Object expandHandler(Handler handler, Object list, Class<Handler> byClass)    {        if (handler==null)            return list;        if (byClass==null || byClass.isAssignableFrom(handler.getClass()))            list=LazyList.add(list, handler);        if (handler instanceof AbstractHandlerContainer)            list=((AbstractHandlerContainer)handler).expandChildren(list, byClass);        else if (handler instanceof HandlerContainer)        {            HandlerContainer container = (HandlerContainer)handler;            Handler[] handlers=byClass==null?container.getChildHandlers():container.getChildHandlersByClass(byClass);            list=LazyList.addArray(list, handlers);        }        return list;    }

至此AbstractHandlerContainer和HandlerContainer就分析完了,值得注意的是虽然AbstractHandlerContainer声称是一个Container,但是确没有定义任何用于存储Handler的容器,甚至没有提供addHandler()之类的方法;实际上这一部分都是在具体子类里实现的,所以AbstractHandlerContainer中定义的方法是不多的。

2.HandlerWrapper

HandlerWrapper正如其名字所表示的是一个包装类,它里面持有一个protected Handler _handler 的属性就是其代理的Handler对象,并为这个属性提供了get()和set()方法:

 //获取当前HandlerWrapper包装的Handler对象 public Handler getHandler(){    return _handler; }  //设置当前HandlerWrapper的包装Handler对象,注意里面有对server引用的一些更新操作  public void setHandler(Handler handler) {        if (isStarted()) {            throw new IllegalStateException(STARTED);        }        Handler old_handler = _handler;        _handler = handler;        if (handler != null) {//设置当前管理的server对象            handler.setServer(getServer());        }        if (getServer() != null) {//更新server对象中的引用            getServer().getContainer().update(this, old_handler, handler, "handler");        }    }

HandlerWrapper的生命周期方法doStart()、doStop(),处理请求的方法handle()等都是直接调用的_handler相应的方法,简单的进行转发没有自己的处理逻辑(在一些子类里是添加了一些逻辑的)。

   /*   * @see org.eclipse.thread.AbstractLifeCycle#doStart()   */    @Override    protected void doStart() throws Exception {        if (_handler != null) {            _handler.start();        }        super.doStart();    }    /*     * @see org.eclipse.thread.AbstractLifeCycle#doStop()     */    @Override    protected void doStop() throws Exception {        if (_handler != null) {            _handler.stop();        }        super.doStop();    }    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {        if (_handler != null && isStarted()) {            _handler.handle(target, baseRequest, request, response);        }    }

从继承关系上可以看到HandlerWrapper还是一个HandlerContainer,这个容器中持有的唯一Handler对象就是被包装的_handler对象,下面是重写的getHandlers()方法:

public Handler[] getHandlers()    {        if (_handler==null)            return new Handler[0];        return new Handler[] {_handler};    }

HandlerWrapper中还重写了expandChildren()方法,将持有的_handler对象传给expandHandler()来进行处理来获取容器中存放的所有handler。

 @Override    protected Object expandChildren(Object list, Class byClass) {        return expandHandler(_handler, list, byClass);    }

这样重载之后调用getChildHandlerByClass()方法返回的就是_handler对象了,这一点很重要在ScopeHandler中的doScope()方法中需要用到这个方法。

在Jetty中HandlerWrapper一个很重要的作用就是形成Handler链,所谓的Handler链就是指几个Handler组合成链式结构,对第一个handler调用handle()方法后整条链上的各个handler的handle()都会被依次调用。如下面的代码所示:

 //handlerA handlerB都是HandlerWrapper类型,handlerC是普通Handler类型        HandlerWrapper handlerA;        HandlerWrapper handlerB;        Handler  handlerC;        //组合成handler链        handlerA.setHandler(handlerB);        handlerB.setHandler(handlerC);        handlerA.handle();        //调用顺序: handlerA.handle()--->handlerB.handle()--->handlerC.handle();

根据上面HandlerWrapper的handle()方法,这种行为其实很好理解。形成这种handler链的好处在于可以方便请求的处理,比如实际中WebAppContext–>SessionHandler—>SecurityHandler—>ServletHandler形成了handler链,那么将请求交给WebAppContext之后,就可以经过不同的Handler处理最后交给ServletHandler从而交给应用程序处理;如果不需要SecurityHandler或SessionHandler,将它们从这个Handler链中去掉就可以了。

3.ScopedHandler


ScopedHandler相比如它的父类HandleWrapper最重大的改变是重写了handle()方法,在HandleWrapper链式handle()调用的基础上引入了doScope()流程。

  @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);        }    }

上面的代码中是根据_outScope是否为null来判断是走doScope()还是doHandle()方法的。_outScope是ScopedHandler引入的一个辅助变量,此外还有一个_nextScope变量。

    protected ScopedHandler _outerScope;    protected ScopedHandler _nextScope;

下面通过距离来说明这两个变量的含义。我们知道ScopedHandler继承自HandlerWrapper,所以也是可以形成Handler链的,下面就是三个ScopedHandler对象形成的handler链:

        ScopedHandler scopedA;        ScopedHandler scopedB;        ScopedHandler scopedC;        scopedA.setHandler(scopedB);        scopedB.setHandler(scopedC);

经过上面的设置之后,形成的handler链是:scopedA–>scopedB–>scopedC。这种情况下,scopedA的_nextScope=scopedB,scopedB的_nextScope=scopedC,scopedC的_nextScope是null(因为它是链尾,没有下一个节点),即_nextScope指向的是当前节点下一个节点的引用;而scopedA的_outerScope是null,scopedB和scopedC的_outScope都是指向scopedA,即_outScope指向的是当前handler链的头结点,对于头结点本身_outScope为null。
弄清楚了_outScope和_nextScope的含义,那么下一个问题就是对于一个ScopeHandler对象如何设置这两个值以及在何时设置这两个值。答案是在组件启动的时候,下面是ScopedHandler中的doStart()方法源码:

/* ------------------------------------------------------------ */    /**     * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart()     */    @Override    protected void doStart() throws Exception    {        try        {           //__outerScope是一个ThreadLocal变量,相当于线程中的全局变量            _outerScope=__outerScope.get();            if (_outerScope==null)               //将handler链头结点的引用填充进去                __outerScope.set(this);            super.doStart();            _nextScope=(ScopedHandler)getChildHandlerByClass(ScopedHandler.class);        }        finally        {            if (_outerScope==null)                __outerScope.set(null);        }    }

上面出现了__outerScope.get()这个方法,这里的__outerScope是ScopedHandler持有的一个ThreadLocal类型变量,在这里主要用于在线程中共享头结点引用的值,因为这个值是不能通过方法参数在handler链中传递的。现在还是以上面提到的scopedA–>scopedB–>scopedC这条handler链解释启动过程:

  1. scopedA调用start()方法,然后进入到它的doStart()方法,这时候__outerScope.get()取出来的是null(因为还没有进行填充),所以scopedA的_outScope的值为null,然后scopeA对象被填充到__scopeHanlder中,并调用super.doStart()。
  2. 因为scopedA是一个HandlerWrapper类型,并且持有的_handler引用指向的是scopedB,所以super.doStart()实际上会调用scopedB的start()方法;这个方法里同样会执行上面的doStart()逻辑,不过这次__outerScope.get()方法返回的不是null而是scopedA的引用,所以scopedB的_outScope被设置为scopedA。这里的super.dostart()会进入到scopedC,也会将scopedC的_outScope指向scopedA。
  3. 因为scopedC的_handler属性为null(因为它没有设置包装对象),所以super.doStart()会返回并执行
    _nextScope=(ScopedHandler)getChildHandlerByClass(ScopedHandler.class)
    这句代码,前面说过对于HandlerWrapper来说getChildHandlerByClass返回的就是其包装的_handler对象,这里返回的就是null。所以scopedC的_nextScope为null,这段方法结束返回scopedB中的doStart()中,同样执行上面这个方法,因为scopedB的_handler引用指向的是scopedC,所以返回的结果就是scopedC的引用即scopedB的_nextScope指向scopeC。
  4. 同理scopedA的_nextScope会指向scopedB。返回scopedA的doStart()方法之后,因为其_outScope为null,所以doStart()中finally部分的逻辑被触发,这个线程的ThreadLocal变量又被设置为null。
        finally        {            if (_outerScope == null) {                __outerScope.set(null);            }        }

最后一个问题是在ScopedHandler形成的Handler链中是如何组织doScope()方法和doHandle()等方法调用的。实际上在ScopedHandler中对于doScope()和doHandle()方法是没有具体实现的,但是提供了nextHandle()和nextScope()两个方法,下面是它们的源码:

 public abstract void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;    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);        } else {            doHandle(target, baseRequest, request, response);        }    }    public abstract void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;    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);        }    }

从nextHandle()和nextScope()方法大致上可以猜到doScope()和doHandle()的调用流程。下面通过ScopedHandler源码注释中提供的两个例子来说明下具体调用顺序:

* <p>For example if Scoped handlers A, B & C were chained together, then  * the calling order would be:<pre> * A.handle(...) *   A.doScope(...) *     B.doScope(...) *       C.doScope(...) *         A.doHandle(...) *           B.doHandle(...) *              C.doHandle(...)    * <pre>

上面的例子是链上所有节点都是ScopedHandler的情况,下面是链上有部分节点不是ScopedHandler的情况:

* <p>If non scoped handler X was in the chained A, B, X & C, then  * the calling order would be:<pre> * A.handle(...) *   A.doScope(...) *     B.doScope(...) *       C.doScope(...) *         A.doHandle(...) *           B.doHandle(...) *             X.handle(...) *               C.handle(...) *                 C.doHandle(...)    * <pre>

无论哪种情况都能保证ScopeHandler链上的doScope()方法在dohandle()、handle()方法之前执行,并且不同对象的doScope()都是按对象在链上的先后执行的,doHandle()和handle()方法也是一样。这样的执行顺序可以使得组件可以在调用dohandle()或handle()处理请求之前进行一些初始化或配置工作,具体的应用场景会在后面分析具体组件的时候涉及到。

原创粉丝点击