设为首页 收藏本站
查看: 784|回复: 0

[经验分享] jetty的ServletHandler分析(servlet最终调用前的一些流程)

[复制链接]

尚未签到

发表于 2017-2-27 11:03:45 | 显示全部楼层 |阅读模式
前面很多文章都提到过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 



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

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



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

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



  • //更新与名字关联的map,filter的namemap与servlet的namemap  
  • protected synchronized void updateNameMappings() {     
  •     _filterNameMap.clear();  //清空filter的name关联map  
  •     if (_filters!=null) {     //从xml文件中定义的filter会保存在这个数组中  
  •         for (int i=0;i<_filters.length;i++) {  //遍历所有定义的filter  
  •             _filterNameMap.put(_filters.getName(),_filters);  //这里key是名字,value是filterHolder  
  •             _filters.setServletHandler(this);  
  •         }  
  •     }  
  •   
  •     _servletNameMap.clear();  //清空servlet的与name关联的map  
  •     if (_servlets!=null) {     
  •         for (int i=0;i<_servlets.length;i++) {  //遍历所有定义的servlet  
  •             _servletNameMap.put(_servlets.getName(),_servlets);  
  •             _servlets.setServletHandler(this);  
  •         }  
  •     }  
  • }  

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



  • //这里主要是是设置pathmap  
  • protected synchronized void updateMappings() {     
  •     if (_filterMappings==null) {  
  •         _filterPathMappings=null;  
  •         _filterNameMappings=null;  
  •     } else {  
  •         _filterPathMappings=new ArrayList();  
  •         _filterNameMappings=new MultiMap();  
  •         //遍历xml中定义的所有的fitler的map信息  
  •         for (int i=0;i<_filterMappings.length;i++)  {  
  •             //获取相应的filterholder  
  •             FilterHolder filter_holder = (FilterHolder)_filterNameMap.get(_filterMappings.getFilterName());  
  •             if (filter_holder==null)  
  •                 throw new IllegalStateException("No filter named "+_filterMappings.getFilterName());  
  •             _filterMappings.setFilterHolder(filter_holder);      
  •             if (_filterMappings.getPathSpecs()!=null)  
  •                 _filterPathMappings.add(_filterMappings);  //如果需要path,那么将其放到pathMap里面去  
  •               
  •             //有的filter指定了servlet,那么需要将servlet的名字与当前的filterHolder对应  
  •             if (_filterMappings.getServletNames()!=null) {  
  •                 String[] names=_filterMappings.getServletNames();  
  •                 for (int j=0;j<names.length;j++)  
  •                 {  
  •                     if (names[j]!=null)  
  •                         _filterNameMappings.add(names[j], _filterMappings);    //这里key是servlet的名字  
  •                 }  
  •             }  
  •         }  
  •     }  
  •   
  •     //将path与servletholder对应起来  
  •     if (_servletMappings==null || _servletNameMap==null) {  
  •         _servletPathMap=null;  
  •     } else {  
  •         PathMap pm = new PathMap();  //创建pathmap  
  •           
  •         //遍历所有从xml文件中读出的servlet的map信息,保存的是用户定义的servlet与其map的信息,一个servlet可能会map到多个path  
  •         for (int i=0;i<_servletMappings.length;i++) {  
  •             //获取servlethodler  
  •             ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings.getServletName());  
  •             if (servlet_holder==null) {  
  •                 throw new IllegalStateException("No such servlet: "+_servletMappings.getServletName());  
  •             } else if (_servletMappings.getPathSpecs()!=null) {    
  •                 String[] pathSpecs = _servletMappings.getPathSpecs();  //获取这个servlet指定的要处理的path  
  •                 for (int j=0;j<pathSpecs.length;j++)  
  •                     if (pathSpecs[j]!=null)  
  •                         pm.put(pathSpecs[j],servlet_holder);  //将path与servletholder放到pahtmap里面去  
  •             }  
  •         }  
  •           
  •         _servletPathMap=pm;  
  •     }  
  •       
  •     if (Log.isDebugEnabled())  {  
  •         Log.debug("filterNameMap="+_filterNameMap);  
  •         Log.debug("pathFilters="+_filterPathMappings);  
  •         Log.debug("servletFilterMap="+_filterNameMappings);  
  •         Log.debug("servletPathMap="+_servletPathMap);  
  •         Log.debug("servletNameMap="+_servletNameMap);  
  •     }     
  •     try {    
  •         if (isStarted()) {  
  •             initialize();  
  •         }  
  •     } catch (Exception e)  {  
  •         throw new RuntimeException(e);  
  •     }  
  • }  

 
代码也还算是比较的简单吧,这里遍历的对象是我们在xml配置文件中定义的一些mapping项,然后进行相应的处理。。
例如,对于filter,我们可以定义它处理的path,也可以为其指定特定的servlet,因此在filter这里需要维护filterPathMapping与filterNameMapping两个对应关系。。。
对于servlet的处理就稍微要简单些了,主需要处理与path相关的对应就可以了,将其放到pathMap里面。。。
 
[java] view plaincopy 



  • //这里的target就是处理过的了,去掉了前面的contextPath,然后删去了最后的参数  
  • //这里会用target来匹配住合适的servlet来处理  
  • public void handle(String target, HttpServletRequest request,HttpServletResponse response, int type)  
  •      throws IOException, ServletException {  
  •     if (!isStarted())  
  •         return;  
  •   
  •     // Get the base requests  
  •     //其实这里获取base_request一般情况下就是当前的request  
  •     final Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();  
  •     //获取上次这个request的处理的一些信息,为了待会处理失败之后可以恢复现场  
  •     final String old_servlet_name=base_request.getServletName();  //上一次request处理的servlet的名字(要知道request对象也是会复用的)  
  •     final String old_servlet_path=base_request.getServletPath();  
  •     final String old_path_info=base_request.getPathInfo();  
  •     final Map old_role_map=base_request.getRoleMap();  
  •       
  •     try {  
  •         ServletHolder servlet_holder=null;  //待会用这个指向找出来的servletHolder  
  •         FilterChain chain=null;  
  •           
  •         // find the servlet  
  •         if (target.startsWith("/")) {    
  •             PathMap.Entry entry=getHolderEntry(target);   //通过targer的匹配,找出合适的servletholder  
  •             if (entry!=null) { //如果可以找到合适的servlet    
  •                 servlet_holder = (ServletHolder)entry.getValue();   //获取相应的servletHodler  
  •                 base_request.setServletName(servlet_holder.getName());  //设置servlet的一些基本信息  
  •                 base_request.setRoleMap(servlet_holder.getRoleMap());  
  •                 if(Log.isDebugEnabled())Log.debug("servlet="+servlet_holder);  
  •                   
  •                 String servlet_path_spec=(String)entry.getKey();   //获取匹配到的servlet的path信息  
  •                 String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);  
  •                 String path_info=PathMap.pathInfo(servlet_path_spec,target);  
  •                   
  •                 if (type==INCLUDE) {  
  •                     base_request.setAttribute(Dispatcher.__INCLUDE_SERVLET_PATH,servlet_path);  
  •                     base_request.setAttribute(Dispatcher.__INCLUDE_PATH_INFO, path_info);  
  •                 } else {  
  •                     base_request.setServletPath(servlet_path);  
  •                     base_request.setPathInfo(path_info);  
  •                 }  
  •                   
  •                 if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0) {  
  •                     chain=getFilterChain(type, target, servlet_holder);  //组装一个filter链,待会处理会先调用filter,然后在调用servlet  
  •                 }  
  •             }        
  •         } else { //如果targer不是以“/”开始的,那么表示不是以path来匹配的,所以要用名字来找servlet  
  •             servlet_holder=(ServletHolder)_servletNameMap.get(target);  //通过名字来寻找servletholder  
  •             if (servlet_holder!=null && _filterMappings!=null && _filterMappings.length>0)  
  •             {  
  •                 base_request.setServletName(servlet_holder.getName());  
  •                 chain=getFilterChain(type, null,servlet_holder);  //构成链  
  •             }  
  •         }  
  •   
  •         if (Log.isDebugEnabled())  {  
  •             Log.debug("chain="+chain);  
  •             Log.debug("servlet holder="+servlet_holder);  
  •         }  
  •   
  •         // Do the filter/handling thang  
  •         //先进行filter的操作,然后处理http请求  
  •         if (servlet_holder!=null)  
  •         {  
  •             base_request.setHandled(true);  //这里设置一个标志位,表示这个http请求已经处理过了  
  •             if (chain!=null) {   //如果有filter链,那么还需要先从filter来开始执行  
  •                 chain.doFilter(request, response);  
  •             } else {  //如果没有的话,那么直接调用servletholder的handle方法来处理就好了,这期其实也就是调用实际的servlet的service方法  
  •                 servlet_holder.handle(request,response);  
  •             }  
  •         } else {  
  •             notFound(request, response);  //这里表示找不到处理  
  •         }  
  •     }  

  


 
代码应该很容易就能够看明白吧,这里首先需要根据target来获取相应的servletHolder,而且然后还要获取需要执行的filter,将他们构成链条,在filter执行完了以后再经由servlet来处理这次http请求。。。。
这里通过target来获取相应的servletholder调用的是getHolderEntry方法,其实这个方法的定义也非常的简单,无非就是直接从前面提到的pathMap里面获得相应的servletHolder就好了。。。
 
好了,这篇文章之后,应该对jetty的整个http的处理流程就已经很清楚了。。。。
可能以后关于jetty的文章都是这种比较细节的了吧,毕竟整体的东西都差不多了。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347888-1-1.html 上篇帖子: 用maven构建SpringMVC+JPA+Hibernate+Hsql+jetty的web项目 下篇帖子: 用Maven构建Java Web开发环境(Jetty容器)之一(转载)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表