Jetty之ContextHandler

这篇文章要分析的类型是非常重要的一个类型...

ContextHandler,从名字上来看就是上下文的handler...

这里普及一下知识:每一个WebApp都对应相应一个context,那么也就对应一个contextHandler当servlet容器收到外部的http请求之后,会根据其请求的path信息来找到相应的webapplication来处理,也就是要找到对应的contextHandler来处理 ,这里也就知道了contextHandler的最重要的作用,那就是将属于当前web的http请求交由内部对应的servlet来处理。。。

由于这个类型的代码比较多,那么就不贴出所有的代码了。。。将其中一些重要的属性和方法抽出来分析一下就好了。。。(ContextHandler继承自HandlerWrapper)

首先来看一个线程变量的定义:

[java] view plaincopy
  1. private static ThreadLocal __context=new ThreadLocal();  //当前的线程变量,用于保存到当前的servletContext  

这个线程变量用于储存当前的servletContext,这个是在servlet标准中定义的接口,jetty中有自己的实现。。其定义就在ContextHandler类型中,一个内部类。。。

接下来是另外的属性定义:

[java] view plaincopy
  1. protected SContext _scontext;   //相应的servletcontext  
  2.   
  3. private AttributesMap _attributes;  //属性,  
  4. private AttributesMap _contextAttributes;  //context的属性  
  5. private ClassLoader _classLoader;  //用于加载app的class  
  6. private String _contextPath="/";  //例如/manager什么的  
  7. private Map _initParams;  //初始化参数  
  8. private String _displayName;  
  9. private Resource _baseResource;    //app部署的根目录 file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/  
  10. private MimeTypes _mimeTypes;  //文件类型对应  
  11. private Map _localeEncodingMap;  
  12. private String[] _welcomeFiles;  //首页的路径  
  13. private ErrorHandler _errorHandler;   //错误handler  
  14. private String[] _vhosts;  //虚拟主机名  
  15. private Set _connectors;   //其所用的connector  
  16. private EventListener[] _eventListeners;  
  17. private Logger _logger;  
  18. private boolean _shutdown;  
  19. private boolean _allowNullPathInfo;  
  20. private int _maxFormContentSize=Integer.getInteger("org.mortbay.jetty.Request.maxFormContentSize",200000).intValue();  
  21. private boolean _compactPath=false;  
  22.   
  23. //一些lisntener的定义  
  24. private Object _contextListeners;    //contextListener  
  25. private Object _contextAttributeListeners;  
  26. private Object _requestListeners;  
  27. private Object _requestAttributeListeners;  
  28. private Set _managedAttributes;  

这里的scontext就是servlet的servlContext,contextPath是当前web的path,我们如果在servlet容器中启动多个app,那么每个webapplication的名字就是它的contextPath。。。。另外还有一些其他的属性,例如当前部署的根目录,一些初始化参数什么的。。。。

还有就是一些listener,例如contextListener什么的。。。

那么接下来我们来看看它的最重要的方法,handle方法:

[java] view plaincopy
  1. //handle方法,用于处理远程的http请求,这里的target就是http请求的path,例如/manager/hello,这里其实是用于判断当前请求的path什么的,如果属于当前的webapp,那么进行处理  
  2. public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)  
  3.         throws IOException, ServletException  {  
  4.     boolean new_context=false;  
  5.     SContext old_context=null;  
  6.     String old_context_path=null;  
  7.     String old_servlet_path=null;  
  8.     String old_path_info=null;  
  9.     ClassLoader old_classloader=null;  
  10.     Thread current_thread=null;  
  11.     //获取当前的http请求  
  12.     Request base_request=(request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest();  
  13.     if( !isStarted() || _shutdown || (dispatch==REQUEST && base_request.isHandled()))  
  14.         return;  
  15.       
  16.     old_context=base_request.getContext();  //获取servletContext,这里一般情况下都是null,因为新建的socket连接发起的http请求  
  17.       
  18.     // Are we already in this context?  
  19.     if (old_context!=_scontext) {  
  20.         new_context=true;  
  21.         //如果有配置虚拟服务器名字,那么需要判断  
  22.         if (_vhosts!=null && _vhosts.length>0)  
  23.         {  
  24.             String vhost = normalizeHostname( request.getServerName());  
  25.             boolean match=false;  
  26.               
  27.             // TODO non-linear lookup  
  28.             for (int i=0;!match && i<_vhosts.length;i++)  
  29.             {  
  30.                 String contextVhost = _vhosts[i];  
  31.                 if(contextVhost==nullcontinue;  
  32.                 if(contextVhost.startsWith("*.")) {  
  33.                     // wildcard only at the beginning, and only for one additional subdomain level  
  34.                     match=contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".")+1,contextVhost.length()-2);  
  35.                 } else  
  36.                     match=contextVhost.equalsIgnoreCase(vhost);  
  37.             }  
  38.             if (!match)  
  39.                 return;  
  40.         }  
  41.           
  42.        //检查connector  
  43.         if (_connectors!=null && _connectors.size()>0)  
  44.         {  
  45.             String connector=HttpConnection.getCurrentConnection().getConnector().getName();  
  46.             if (connector==null || !_connectors.contains(connector))  
  47.                 return;  
  48.         }  
  49.           
  50.         // Nope - so check the target.  
  51.         if (dispatch==REQUEST)  
  52.         {  
  53.             if (_compactPath) {  
  54.                 target=URIUtil.compactPath(target);  
  55.             }  
  56.               
  57.             if (target.equals(_contextPath)){ //如果这里http请求的path等于contextPath,那么表示真正的pathinfo是空的,例如/manager,(启动多个app的话,会自动用app的名字来作为contextPath的前缀)  
  58.                 if (!_allowNullPathInfo && !target.endsWith(URIUtil.SLASH)) { //判断是否运行空的path  
  59.                     base_request.setHandled(true);  
  60.                     if (request.getQueryString()!=null)  
  61.                         response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)+"?"+request.getQueryString());  
  62.                     else   
  63.                         response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH));  
  64.                     return;  
  65.                 }  
  66.                 if (_contextPath.length()>1)  
  67.                 {  
  68.                     target=URIUtil.SLASH;  
  69.                     request.setAttribute("org.mortbay.jetty.nullPathInfo",target);  
  70.                 }  
  71.             } else if (target.startsWith(_contextPath) && (_contextPath.length()==1 || target.charAt(_contextPath.length())=='/')){  
  72.                //这里看http的请求path是否已当前的contextPaht开始  
  73.                 if (_contextPath.length()>1)  
  74.                     target=target.substring(_contextPath.length());  
  75.             } else  {  
  76.                 //不是这个context该处理的,也就是不是这个webapplication该处理的  
  77.                 return;  
  78.             }  
  79.         }  
  80.     }  
  81.       
  82.     try  
  83.     {  
  84.         old_context_path=base_request.getContextPath();  //这里一般是空,因为刚开始没有设置  
  85.         old_servlet_path=base_request.getServletPath();  
  86.         old_path_info=base_request.getPathInfo();  
  87.           
  88.         //设置servletContext  
  89.         base_request.setContext(_scontext);  //为当前的http请求设置servletContext  
  90.         if (dispatch!=INCLUDE && target.startsWith("/"))  
  91.         {  
  92.             if (_contextPath.length()==1)  
  93.                 base_request.setContextPath("");  
  94.             else  
  95.                 base_request.setContextPath(_contextPath);  //设置contextPath  
  96.             base_request.setServletPath(null);  
  97.             base_request.setPathInfo(target);  //设置pathInfo  
  98.         }  
  99.   
  100.         ServletRequestEvent event=null;  
  101.         if (new_context) {  
  102.             // Set the classloader  
  103.             //设置当前的classLoader  
  104.             if (_classLoader!=null)  
  105.             {  
  106.                 current_thread=Thread.currentThread();  
  107.                 old_classloader=current_thread.getContextClassLoader();  
  108.                 current_thread.setContextClassLoader(_classLoader);  
  109.             }  
  110.               
  111.             // Handle the REALLY SILLY request events!  
  112.             if (_requestListeners!=null)  
  113.             {  
  114.                 event = new ServletRequestEvent(_scontext,request);  
  115.                 for(int i=0;i<LazyList.size(_requestListeners);i++)  
  116.                     ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(event);  
  117.             }  
  118.             //设置当前request的attributelistener  
  119.             for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)  
  120.                 base_request.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));  
  121.         }  
  122.           
  123.         // Handle the request  
  124.         try  {  
  125.             if (dispatch==REQUEST && isProtectedTarget(target))  //判断是否访问的是保护的资源,如果是的话,返回notfound,WEB-INF文件夹下面是保护资源  
  126.                 throw new HttpException(HttpServletResponse.SC_NOT_FOUND);  
  127.             //获取内部的handler来处理  
  128.             Handler handler = getHandler();  
  129.             //处理当前的http请求,这里其实是jetty.servlet.SessionHandler,不过最终还是调用ServletHandler  
  130.             if (handler!=null)  
  131.                 handler.handle(target, request, response, dispatch);  
  132.         }  
  133.         catch(HttpException e)  
  134.         {  
  135.             Log.debug(e);  
  136.             response.sendError(e.getStatus(), e.getReason());  
  137.         }  
  138.         finally  
  139.         {  
  140.             // Handle more REALLY SILLY request events!  
  141.             if (new_context)  
  142.             {  
  143.                 for(int i=LazyList.size(_requestListeners);i-->0;)  
  144.                     ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(event);  
  145.                   
  146.                 for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)  
  147.                     base_request.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));  
  148.             }  
  149.         }  
  150.     }  
  151.     finally  
  152.     {  
  153.         if (old_context!=_scontext)  
  154.         {  
  155.             // reset the classloader  
  156.             if (_classLoader!=null)  
  157.             {  
  158.                 current_thread.setContextClassLoader(old_classloader);  
  159.             }  
  160.               
  161.             // reset the context and servlet path.  
  162.             base_request.setContext(old_context);  
  163.             base_request.setContextPath(old_context_path);  
  164.             base_request.setServletPath(old_servlet_path);  
  165.             base_request.setPathInfo(old_path_info);   
  166.         }  
  167.     }  
  168. }  

这个方法就最终将会用于将http请求交给对应的servlet来处理。。。

这里的处理流程如下:

(1)判断当前的http请求的path与当前web程序的contextPath是否对应,如果不对应的话那么说明这个http请求不该由当前来处理。。。

(2)如果对应的话,那么设置当前这个request的servletContext,当前线程的classLoader等,

(3)调用内部的handler来处理这个request,这里一般情况下都是SessionHandler,不过SessionHandler其实最终也会调用ServletHandler来处理这个request,将其交给最终的servlet来处理。。。其实这也是jetty的链式调用的最重要利用。

(4)一些收尾的工作。。。。


好了,这里其实对整个ContextHandler的基本的一些了解就算差不多了。。。起码算是知道它用来干嘛的了。。至于说它的一些细节,就交给以后的运行分析再来说吧。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值