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链解释启动过程:
- scopedA调用start()方法,然后进入到它的doStart()方法,这时候__outerScope.get()取出来的是null(因为还没有进行填充),所以scopedA的_outScope的值为null,然后scopeA对象被填充到__scopeHanlder中,并调用super.doStart()。
- 因为scopedA是一个HandlerWrapper类型,并且持有的_handler引用指向的是scopedB,所以super.doStart()实际上会调用scopedB的start()方法;这个方法里同样会执行上面的doStart()逻辑,不过这次__outerScope.get()方法返回的不是null而是scopedA的引用,所以scopedB的_outScope被设置为scopedA。这里的super.dostart()会进入到scopedC,也会将scopedC的_outScope指向scopedA。
- 因为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。 - 同理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()处理请求之前进行一些初始化或配置工作,具体的应用场景会在后面分析具体组件的时候涉及到。
- Jetty源码分析之ScopedHandler及Handler链
- Jetty源码分析之AbstractHandler
- Jetty源码分析之ContextHandler
- Jetty源码分析之WebAppContext
- jetty介绍之handler
- jetty 介绍之handler
- jetty介绍之handler
- Jetty源码分析之线程池:QueuedThreadPool
- Handler作用及部分源码分析
- 消息机制Handler及相关源码分析
- Handler原理讲解及源码分析
- Jetty 源码分析
- Jetty 源码分析(转移)
- Jetty 源码分析
- Jetty 源码分析
- jetty 源码分析
- Jetty 源码分析
- Jetty 源码分析
- MyBatis框架的使用
- 面试题7:用两个栈实现队列
- myBatis连接超时问题
- Jboss环境,Spring3.1.2与Hadoop2.3.0冲突解决方案
- ELK + Filebeat 搭建日志系统
- Jetty源码分析之ScopedHandler及Handler链
- window平台上基于python搭建一个堡垒级系统
- 12.20学习计划
- JS的事件响应,与网页交互
- 第十二次学习报告
- tf.Session
- 中兴坠楼事件,愿程序员的世界里,不会再有悲剧
- git 理解 HEAD^与HEAD~
- 脏读、幻读、不可重复读