jetty源码分析2-handlers
1.handler类图和时序
先上一个handler的继承体系结构图
从上图可以看到,jetty通过一级一级的继承,不断的扩展handler的功能,从最简单的处理到复杂的web容器,下面一一分析之.
?
在分析之前,再看一个简单的请求的handler调用过程:
at com.ali.b2b.crm.base.common.filter.LocaleFilter.doFilter(LocaleFilter.java:47)at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1322)at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:473)at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:514)at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:920)at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:403)at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:184)at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:856)at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:247)at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:151)at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:114)at org.eclipse.jetty.server.Server.handle(Server.java:352)at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:596)at org.eclipse.jetty.server.HttpConnection$RequestHandler.content(HttpConnection.java:1066)at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:805)at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)at org.eclipse.jetty.server.HttpConnection.handle(HttpConnection.java:426)at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:510)at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:40)at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:450)at java.lang.Thread.run(Thread.java:619)
?可以看到,在一个请求的过程中,各个一般也是多个handler组合使用的。
?
2.handler继承体系分析2.1 handler接口public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
?
参数解释:
在server解析完http头,构造好request和response传入第一个handler的时候,base quest和quest是一样的,参见server的handle方法:
public void handle(HttpConnection connection) throws IOException, ServletException { final String target=connection.getRequest().getPathInfo(); final Request request=connection.getRequest(); final Response response=connection.getResponse(); handle(target, request, request, response); } public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (_handler!=null && isStarted()) _handler.handle(target,baseRequest, request, response); }
?
上面源码的第一个handle方法,是在解析完http头之后,由parser回调httpconnection的接口,然后再调用sever的这个handle方法,这个是所有请求进入handler体系的入口。而下面这个方法,是server作为一个handler实现的handler接口。当然,handler继承了lifecycle接口,具有和server相同的生命周期。
??? 一般要自己写handler都会继承于它,AbstractHandler最主要就是持有了一个对于server的引用,同时提供了lifeCycle的最简实现。
目前除了AbstractHandlerContainer外,还有有3个直接继承于abstractHandler的最终实现是DefaultHandler,ResourceHandler和Redirector,看名字大家应该都能猜到这3个handler的作用了。
2.3 AbstractHandlerContainer??? 该类继承自AbstractHandler,同时实现了HandlerContainer接口,并提供了对其的基本实现。主要有两种实现,一种是HandlerCollection,一种是HandlerWrapper。
HandlerCollection的逻辑如下:
for (int i=0;i<_handlers.length;i++) { _handlers[i].handle(target,baseRequest, request, response); }
它就是顺序的把所有的handler的handle方法执行一遍。
HandlerWrapper的逻辑如下:
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有两个核心的子类,一个是Server,这个比较复杂,以后起专题再谈。
另一个是ScopedHandler,它主要是完成一个handler链的两次循环调用。官方解释是这样的:
?
For example if Scoped handlers A, B & C were chained together, then
the calling order would be:
public class MyServletContextListener implements ServletContextListener{ public void contextInitialized(ServletContextEvent sce) {String myparam = sce.getServletContext().getInitParameter("myparam"); }}
?
事实上是通过ContextHandler的doStart代码的startContext中去调用的
if (_contextListeners != null ) { ServletContextEvent event= new ServletContextEvent(_scontext); for (int i= 0; i < LazyList.size(_contextListeners); i++) { ((ServletContextListener)LazyList.get(_contextListeners, i)).contextInitialized(event); } }
?
基本上ContextHandler的doScope逻辑是如果保存request的上下文,注入新的上下文,待处理完成后再还原以前的上下文,比如contextHandler的简化示例逻辑如下:
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletExceptionold_context=baseRequest.getContext();if (old_context!=_scontext){baseRequest.setContext(_scontext);nextScope();baseRequest.setContext(old_context);}}
??这种情况貌似比较少,从一个contexthandler派发到另外一个contexthandler,开始怀疑是requestDispatcher的情况,后来看代码发现requestDispatcher只做同一个context内部的转发(参考:http://vinfai.iteye.com/blog/483886),现在怀疑是continuation可能会造成,但未验证。
2.6?ServletHandler这个handler将不同的请求分派给不同的servlet,因此它持有所有web.xml下定义的filter和servlet的引用,以及对应的map关系。
在其doScope方法中,取得对应path的servletHolder:
if (target.startsWith("/")){_servletPathMap.getMatch(pathInContext);}else{servlet_holder=(ServletHolder)_servletNameMap.get(target);}baseRequest.setUserIdentityScope(servlet_holder);
?
在其doHandle方法中,取得fileterChain:
ServletHolder servlet_holder=(ServletHolder) baseRequest.getUserIdentityScope();chain=getFilterChain(baseRequest, null,servlet_holder);if (chain!=null){ chain.doFilter(req, res);}else{ servlet_holder.handle(baseRequest,req,res);}
?
2.7?SessionHandlerSessionHandler主要是处理session相关事宜,他的核心属性有一个SessionManager
if (old_session_manager != _sessionManager) { // new session context baseRequest.setSessionManager(_sessionManager); baseRequest.setSession(null); } HttpSession session=null; if (_sessionManager!=null) { session=baseRequest.getSession(false); }
?
这里主要是把_sessionManager set到baseRequest中,具体的每个request的session是在=baseRequest.getSession的时候new出来的。
2.8?ServletContextHandlerServlet Context, ContextHandler的子类,默认有sessionHandler,securityHandler和servletHandler3个元素,默认只有ServletHandler,但在构造函数中可以选择加上session和security的handler。在doStart的过程中,会创建并初始化3个元素handler,并将其构造成nest handler的链模式。
protectedvoid startContext() throws Exception { // force creation of missing handlers. getSessionHandler(); getSecurityHandler(); getServletHandler(); Handler handler = _servletHandler; if (_securityHandler!=null) { _securityHandler.setHandler(handler); handler=_securityHandler; } if (_sessionHandler!=null) { _sessionHandler.setHandler(handler); handler=_sessionHandler; } super.startContext(); _servletHandler.initialize(); }
?
链的顺序就是session–>security–>servlet,可以空缺
?
2.9?WebAppContextServletContextHandler的子类,后面起专题讲。
2.9?ContextHandlerCollectionContextHandlerCollection负责将不同context的请求对应给不同的handler,常规部署中经常是webappContext。
ContextHandlerCollection在请求过程中以请求的最长匹配开始,逐步匹配直到找到合适的contextHandler。注意context的path是设置在各个contexthandler中的。比如我配置一个webAppContext,contextPath为”test”, 一个普通的ContextHandler,设置一个ResourceHandler到其中,contextPath为”test/res”,然后请求 localhost/test/res/a.htm,容器就会先到ResHandler的resourceBase下面去找a.htm,如果找不到,就交给webAppContext去处理。
简化后的代码如下:
Object contexts = map.getLazyMatches(target); for (int i=0; i<LazyList.size(contexts); i++){ // then, match against the virtualhost of each context Map.Entry entry = (Map.Entry)LazyList.get(contexts, i); Object list = entry.getValue(); for (int j=0; j<LazyList.size(list); j++){ Handler handler = (Handler)LazyList.get(list,j); handler.handle(target,baseRequest, request, response); if (baseRequest.isHandled()) return; } }
?