Jetty的ServletHandler分析

来源:互联网 发布:sql查看字符长度 编辑:程序博客网 时间:2024/05/19 16:05

前面很多文章都提到过ServletHandler这种类型,它在整个http的处理中所占的位置如下:

(1)connector -> (2)server->(3)contextHandlerCollection->(4)WebAppContext->(5)ServletHandler->(6)Servlet

从上面整个http的处理过程来看,ServletHandler应该算得上是最接近用户定义的servlet的了。。。

一般情况下context收到http请求之后,这个请求最终都会直接交到servletHandler来处理,因此它也需要负责对http请求的path进行处理,从而才能将这个http请求交给正确的servlet。。。。


好了,先来看看ServletHandler的一些重要的属性的定义吧:

[java] view plaincopy
  1. private ContextHandler _contextHandler;   //这个servlet所属的contextHandler  
  2. private ContextHandler.SContext _servletContext;  //当前这个context的servletcontext  
  3. private FilterHolder[] _filters;   //filter数组  
  4. private FilterMapping[] _filterMappings;  //xml中的filter的map信息  
  5. private boolean _filterChainsCached=true;  
  6. private int _maxFilterChainsCacheSize=1000;  
  7. private boolean _startWithUnavailable=true;  
  8.   
  9. private ServletHolder[] _servlets;   //servletholder数组,一般情况下用户定义的servlet都会被servletholder包装一下  
  10. private ServletMapping[] _servletMappings;   //用于保存从xml中读取出来的servlet的map信息  
  11.   
  12. private transient Map _filterNameMap= new HashMap();    //filter的name对应  
  13. private transient List _filterPathMappings;   //将filter与特定的path对应起来  
  14. private transient MultiMap _filterNameMappings;  //servlet的name与filter的对应,有的filter可能会指定特定的servlet  
  15.   
  16. private transient Map _servletNameMap=new HashMap();   //servlet与名字进行对应  
  17. private transient PathMap _servletPathMap;   //pathmap,这个很重要,当请求来了之后,就会通过它来匹配出合适的servlet来处理  
  18.   
  19. protected transient HashMap _chainCache[];   //请求访问path与fitlerchain的缓存,防止每次都要创建  

这里最为重要的一个属性应该就是servletPathMap了吧,我们在web.xml文件中会定义许多servlet,最后都会通过  <servlet-mapping> 元素将某个servlet与一个或者多个path对应起来。。。而这最终的对应关系都会在servletPathMap里面。。。另外还有许多其他的属性,具体他们是干什么的,应该注释就说的很清楚了吧。。。

那么接下来我们来看看ServletHandler的doStart方法吧:

[java] view plaincopy
  1. //servlethandler的启动过程  
  2. protected synchronized void doStart()  
  3.     throws Exception {  
  4.     _servletContext=ContextHandler.getCurrentContext();  //获取当前的servletcontext  
  5.     _contextHandler=_servletContext==null?null:_servletContext.getContextHandler(); //保存其所属的contextHandler  
  6.   
  7.     updateNameMappings();   //名字对应map的更新,也就是我们在xml文件中定义的servlet名字与servletholder对象的对应,当然这里还有filter  
  8.     updateMappings();  //处理xml文件中定义的mapping信息,这里最重要要做的事情就是更新pathMap  
  9.       
  10.     if(_filterChainsCached)  //这个数组的构建比较奇怪啊  
  11.         _chainCache=     new HashMap[]{null,new HashMap(),new HashMap(),null,new HashMap(),null,null,null,new HashMap()};  
  12.   
  13.     super.doStart();  
  14.       
  15.     if (_contextHandler==null || !(_contextHandler instanceof Context)) {//一般情况下不会调用,除非直接在server的handler就弄成了ServletHandr,在context的start后会在外面调用init方法  
  16.         initialize();  //这个方法用于从classLoader里面载入servlet的calss文件,并且需要的话,会初始化这个servlet对象  
  17.     }  
  18. }     

代码还算比较的简洁吧,因为要做的事情都调用其他的方法来处理了,这里主要就是要建立两个map,首先是name与filterHolder或者servletHolder的对应关系,接着是path与fitler或者servlet的对应关系。。。。

那么先来看看updateNameMappings方法吧:

[java] view plaincopy
  1. //更新与名字关联的map,filter的namemap与servlet的namemap  
  2. protected synchronized void updateNameMappings() {     
  3.     _filterNameMap.clear();  //清空filter的name关联map  
  4.     if (_filters!=null) {     //从xml文件中定义的filter会保存在这个数组中  
  5.         for (int i=0;i<_filters.length;i++) {  //遍历所有定义的filter  
  6.             _filterNameMap.put(_filters[i].getName(),_filters[i]);  //这里key是名字,value是filterHolder  
  7.             _filters[i].setServletHandler(this);  
  8.         }  
  9.     }  
  10.   
  11.     _servletNameMap.clear();  //清空servlet的与name关联的map  
  12.     if (_servlets!=null) {     
  13.         for (int i=0;i<_servlets.length;i++) {  //遍历所有定义的servlet  
  14.             _servletNameMap.put(_servlets[i].getName(),_servlets[i]);  
  15.             _servlets[i].setServletHandler(this);  
  16.         }  
  17.     }  
  18. }  

这段代码应该算是很简单的吧,这里首先遍历filter数组,里面保存了在启动的时候从默认以及用户定义的xml文件中定义的所有的filter,当然它被包装成了filterHolder,然后将他们放到map里面,key是这个filter的名字,value就是当前这个filterHolder了。。。下面对servlet的处理都是一样的。。。

那么接下来来看看updateMappings方法吧:

[java] view plaincopy
  1. //这里主要是是设置pathmap  
  2. protected synchronized void updateMappings() {     
  3.     if (_filterMappings==null) {  
  4.         _filterPathMappings=null;  
  5.         _filterNameMappings=null;  
  6.     } else {  
  7.         _filterPathMappings=new ArrayList();  
  8.         _filterNameMappings=new MultiMap();  
  9.         //遍历xml中定义的所有的fitler的map信息  
  10.         for (int i=0;i<_filterMappings.length;i++)  {  
  11.             //获取相应的filterholder  
  12.             FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings[i].getFilterName());  
  13.             if (filter_holder==null)  
  14.                 throw new IllegalStateException("No filter named "+_filterMappings[i].getFilterName());  
  15.             _filterMappings[i].setFilterHolder(filter_holder);      
  16.             if (_filterMappings[i].getPathSpecs()!=null)  
  17.                 _filterPathMappings.add(_filterMappings[i]);  //如果需要path,那么将其放到pathMap里面去  
  18.               
  19.             //有的filter指定了servlet,那么需要将servlet的名字与当前的filterHolder对应  
  20.             if (_filterMappings[i].getServletNames()!=null) {  
  21.                 String[] names=_filterMappings[i].getServletNames();  
  22.                 for (int j=0;j<names.length;j++)  
  23.                 {  
  24.                     if (names[j]!=null)  
  25.                         _filterNameMappings.add(names[j], _filterMappings[i]);    //这里key是servlet的名字  
  26.                 }  
  27.             }  
  28.         }  
  29.     }  
  30.   
  31.     //将path与servletholder对应起来  
  32.     if (_servletMappings==null || _servletNameMap==null) {  
  33.         _servletPathMap=null;  
  34.     } else {  
  35.         PathMap pm = new PathMap();  //创建pathmap  
  36.           
  37.         //遍历所有从xml文件中读出的servlet的map信息,保存的是用户定义的servlet与其map的信息,一个servlet可能会map到多个path  
  38.         for (int i=0;i<_servletMappings.length;i++) {  
  39.             //获取servlethodler  
  40.             ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());  
  41.             if (servlet_holder==null) {  
  42.                 throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());  
  43.             } else if (_servletMappings[i].getPathSpecs()!=null) {    
  44.                 String[] pathSpecs = _servletMappings[i].getPathSpecs();  //获取这个servlet指定的要处理的path  
  45.                 for (int j=0;j<pathSpecs.length;j++)  
  46.                     if (pathSpecs[j]!=null)  
  47.                         pm.put(pathSpecs[j],servlet_holder);  //将path与servletholder放到pahtmap里面去  
  48.             }  
  49.         }  
  50.           
  51.         _servletPathMap=pm;  
  52.     }  
  53.       
  54.     if (Log.isDebugEnabled())  {  
  55.         Log.debug("filterNameMap="+_filterNameMap);  
  56.         Log.debug("pathFilters="+_filterPathMappings);  
  57.         Log.debug("servletFilterMap="+_filterNameMappings);  
  58.         Log.debug("servletPathMap="+_servletPathMap);  
  59.         Log.debug("servletNameMap="+_servletNameMap);  
  60.     }     
  61.     try {    
  62.         if (isStarted()) {  
  63.             initialize();  
  64.         }  
  65.     } catch (Exception e)  {  
  66.         throw new RuntimeException(e);  
  67.     }  
  68. }  

代码也还算是比较的简单吧,这里遍历的对象是我们在xml配置文件中定义的一些mapping项,然后进行相应的处理。。

例如,对于filter,我们可以定义它处理的path,也可以为其指定特定的servlet,因此在filter这里需要维护filterPathMapping与filterNameMapping两个对应关系。。。

对于servlet的处理就稍微要简单些了,主需要处理与path相关的对应就可以了,将其放到pathMap里面。。。

[java] view plaincopy
  1. //这里的target就是处理过的了,去掉了前面的contextPath,然后删去了最后的参数  
  2. //这里会用target来匹配住合适的servlet来处理  
  3. public void handle(String target, HttpServletRequest request,HttpServletResponse response, int type)  
  4.      throws IOException, ServletException {  
  5.     if (!isStarted())  
  6.         return;  
  7.   
  8.     // Get the base requests  
  9.     //其实这里获取base_request一般情况下就是当前的request  
  10.     final Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();  
  11.     //获取上次这个request的处理的一些信息,为了待会处理失败之后可以恢复现场  
  12.     final String old_servlet_name=base_request.getServletName();  //上一次request处理的servlet的名字(要知道request对象也是会复用的)  
  13.     final String old_servlet_path=base_request.getServletPath();  
  14.     final String old_path_info=base_request.getPathInfo();  
  15.     final Map old_role_map=base_request.getRoleMap();  
  16.       
  17.     try {  
  18.         ServletHolder servlet_holder=null;  //待会用这个指向找出来的servletHolder  
  19.         FilterChain chain=null;  
  20.           
  21.         // find the servlet  
  22.         if (target.startsWith("/")) {    
  23.             PathMap.Entry entry=getHolderEntry(target);   //通过targer的匹配,找出合适的servletholder  
  24.             if (entry!=null) { //如果可以找到合适的servlet    
  25.                 servlet_holder = (ServletHolder)entry.getValue();   //获取相应的servletHodler  
  26.                 base_request.setServletName(servlet_holder.getName());  //设置servlet的一些基本信息  
  27.                 base_request.setRoleMap(servlet_holder.getRoleMap());  
  28.                 if(Log.isDebugEnabled())Log.debug("servlet="+servlet_holder);  
  29.                   
  30.                 String servlet_path_spec=(String)entry.getKey();   //获取匹配到的servlet的path信息  
  31.                 String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);  
  32.                 String path_info=PathMap.pathInfo(servlet_path_spec,target);  
  33.                   
  34.                 if (type==INCLUDE) {  
  35.                     base_request.setAttribute(Dispatcher.__INCLUDE_SERVLET_PATH,servlet_path);  
  36.                     base_request.setAttribute(Dispatcher.__INCLUDE_PATH_INFO, path_info);  
  37.                 } else {  
  38.                     base_request.setServletPath(servlet_path);  
  39.                     base_request.setPathInfo(path_info);  
  40.                 }  
  41.                   
  42.                 if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0) {  
  43.                     chain=getFilterChain(type, target, servlet_holder);  //组装一个filter链,待会处理会先调用filter,然后在调用servlet  
  44.                 }  
  45.             }        
  46.         } else { //如果targer不是以“/”开始的,那么表示不是以path来匹配的,所以要用名字来找servlet  
  47.             servlet_holder=(ServletHolder)_servletNameMap.get(target);  //通过名字来寻找servletholder  
  48.             if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)  
  49.             {  
  50.                 base_request.setServletName(servlet_holder.getName());  
  51.                 chain=getFilterChain(type, null,servlet_holder);  //构成链  
  52.             }  
  53.         }  
  54.   
  55.         if (Log.isDebugEnabled())  {  
  56.             Log.debug("chain="+chain);  
  57.             Log.debug("servlet holder="+servlet_holder);  
  58.         }  
  59.   
  60.         // Do the filter/handling thang  
  61.         //先进行filter的操作,然后处理http请求  
  62.         if (servlet_holder!=null)  
  63.         {  
  64.             base_request.setHandled(true);  //这里设置一个标志位,表示这个http请求已经处理过了  
  65.             if (chain!=null) {   //如果有filter链,那么还需要先从filter来开始执行  
  66.                 chain.doFilter(request, response);  
  67.             } else {  //如果没有的话,那么直接调用servletholder的handle方法来处理就好了,这期其实也就是调用实际的servlet的service方法  
  68.                 servlet_holder.handle(request,response);  
  69.             }  
  70.         } else {  
  71.             notFound(request, response);  //这里表示找不到处理  
  72.         }  
  73.     }  


代码应该很容易就能够看明白吧,这里首先需要根据target来获取相应的servletHolder,而且然后还要获取需要执行的filter,将他们构成链条,在filter执行完了以后再经由servlet来处理这次http请求。。。。

这里通过target来获取相应的servletholder调用的是getHolderEntry方法,其实这个方法的定义也非常的简单,无非就是直接从前面提到的pathMap里面获得相应的servletHolder就好了。。。


好了,这篇文章之后,应该对jetty的整个http的处理流程就已经很清楚了。。。。

可能以后关于jetty的文章都是这种比较细节的了吧,毕竟整体的东西都差不多了。。。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 韩国交换生签证怎么办 没有钱还贷款怎么办 英国主动退学后怎么办 英国留学被劝退怎么办 英国留学签证被拒怎么办 签证纸丢了怎么办 日本的探亲签证怎么办 成都去港澳怎么办签证 在北京怎么办泰国签证 法院判决不准离婚怎么办 再婚小孩上户口怎么办 被供应商起诉了怎么办 离婚案原告撤诉怎么办 离婚起诉不到场怎么办 判决书判了败诉怎么办 对执行裁定不服怎么办 贴吧尺寸超限怎么办 usbkey密码忘了怎么办 农信房贷逾期几个小时怎么办 广东农信房贷逾期一天怎么办 三亚的房太潮了怎么办 没高中档案积分怎么办 临牌过期了怎么办 居住证凭证丢了怎么办 上海市居住证过期了怎么办 上海居住证积分不够怎么办 居住证登录密码忘记怎么办 投靠中考上海居住证怎么办 嫁入广州户口怎么办 上海积分扣完了怎么办 公立小学积分不够怎么办 查不到户口信息怎么办 小孩上不到户口怎么办 离开上海上海户口怎么办? 上海没房户口怎么办 没有房照动迁怎么办 持有上海居住证怎么办准生证 换公司后公积金怎么办 换工作了住房公积金怎么办 以前买的户口怎么办 上海落户积分不够怎么办