亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Tomcat源碼分析(四)--容器處理鏈接之責(zé)任鏈模式

系統(tǒng) 2120 0

本系列轉(zhuǎn)載自?http://blog.csdn.net/haitao111313/article/category/1179996?
目標(biāo):在這篇文章希望搞明白 connector.getContainer().invoke(request,?response); 調(diào)用容器的invoke后是怎么傳遞到 servlet或者jsp的?

? ?由上篇文章 Tomcat源碼分析(三)--連接器是如何與容器關(guān)聯(lián)的? 可知, connector.getContainer()得到的容器應(yīng)該是 StandardEngine(其實(shí)應(yīng)該是由server.xml文件配置得到的,這里先假定是 StandardEngine ), StandardEngine沒有invoke方法,它繼承與ContainerBase(事實(shí)上所有的容器都繼承于ContainerBase,在ContainerBase類有一些容器的公用方法和屬性),抽象類 ContainerBase 的invoke方法如下:

  1. protected ?Pipeline?pipeline?=? new ?StandardPipeline( this ); //標(biāo)準(zhǔn)管道的實(shí)現(xiàn)StandardPipeline ??
  2. public ? void ?invoke(Request?request,?Response?response)??
  3. ??????? throws ?IOException,?ServletException?{??
  4. ???????pipeline.invoke(request,?response); //調(diào)用管道里的invoke ??
  5. ???}??

由代碼可知 ContainerBase 的invoke方法是傳遞到Pipeline,調(diào)用了Pipeline的invoke方法。這里要說一下Pipeline這個(gè)類,這是一個(gè)管道類,每一個(gè)管道類 Pipeline 包含數(shù)個(gè)閥類,閥類是實(shí)現(xiàn)了Valve接口的類,Valve接口聲明了invoke方法。管道和閥的概念跟servlet編程里面的過濾器機(jī)制非常像, 管道就像過濾器鏈,閥就好比是過濾器 。不過管道中還有一個(gè)基礎(chǔ)閥的概念,所謂基礎(chǔ)閥就是在管道中當(dāng)管道把所有的普通閥都調(diào)用完成后再調(diào)用的。不管是普通閥還是基礎(chǔ)閥,都實(shí)現(xiàn)了Value接口,也都繼承于抽象類ValveBase。在tomcat中,當(dāng)調(diào)用了管道的invoke方法,管道則會(huì)順序調(diào)用它里面的閥的invoke方法。先看看管道StandardPipeline的invoke方法:

  1. public ? void ?invoke(Request?request,?Response?response)??
  2. ???????? throws ?IOException,?ServletException?{??
  3. ???????? //?Invoke?the?first?Valve?in?this?pipeline?for?this?request ??
  4. ????????( new ?StandardPipelineValveContext()).invokeNext(request,?response);??
  5. ????}??

其中StandardPipelineValveContext是管道里的一個(gè)內(nèi)部類,內(nèi)部類的作用是幫助管道順序調(diào)用閥Value的invoke方法,下面看它的定義代碼:

  1. protected ? class ?StandardPipelineValveContext??
  2. ????? implements ?ValveContext?{??
  3. ????? protected ? int ?stage?=? 0 ;??
  4. ????? public ?String?getInfo()?{??
  5. ????????? return ?info;??
  6. ?????}??
  7. ????? public ? void ?invokeNext(Request?request,?Response?response)??
  8. ????????? throws ?IOException,?ServletException?{??
  9. ????????? int ?subscript?=?stage; //閥的訪問變量 ??
  10. ?????????stage?=?stage?+? 1 ; //當(dāng)前訪問到第幾個(gè)閥 ??
  11. ????????? //?Invoke?the?requested?Valve?for?the?current?request?thread ??
  12. ????????? if ?(subscript?<?valves.length)?{??
  13. ?????????????valves[subscript].invoke(request,?response,? this ); //管道的閥數(shù)組 ??
  14. ?????????}? else ? if ?((subscript?==?valves.length)?&&?(basic?!=? null ))?{??
  15. ?????????????basic.invoke(request,?response,? this ); //當(dāng)基礎(chǔ)閥調(diào)用完成后,調(diào)用管道的基礎(chǔ)閥的invoke閥 ??
  16. ?????????}? else ?{??
  17. ????????????? throw ? new ?ServletException??
  18. ?????????????????(sm.getString( "standardPipeline.noValve" ));??
  19. ?????????}??
  20. ?????}??
  21. ?}??
內(nèi)部類 StandardPipelineValveContext的invokeNext方法通過使用局部變量來訪問下一個(gè)管道數(shù)組,管道類的變量stage保存當(dāng)前訪問到第幾個(gè)閥,valves保存管道的所有閥, 在調(diào)用普通閥的invoke方法是,會(huì)把內(nèi)部類 StandardPipelineValveContext本身傳進(jìn)去,這樣在普通閥中就能調(diào)用invokeNext方法以便訪問下一個(gè)閥的invoke方法 ,下面 看一個(gè)普通閥的invoke方法:

  1. public ? void ?invoke(Request?request,?Response?response,?ValveContext?valveContext)??
  2. ?? throws ?IOException,?ServletException?{??
  3. ?? //?Pass?this?request?on?to?the?next?valve?in?our?pipeline ??
  4. ??valveContext.invokeNext(request,?response); //使用調(diào)用下一個(gè)閥的invoke方法 ??
  5. ??System.out.println( "Client?IP?Logger?Valve" );??
  6. ??ServletRequest?sreq?=?request.getRequest();??
  7. ??System.out.println(sreq.getRemoteAddr());??
  8. ??System.out.println( "------------------------------------" );??
  9. }??
這個(gè)閥的invoke方法,通過傳進(jìn)來到 StandardPipelineValveContext(實(shí)現(xiàn)了ValveContext接口 )的invokeNext方法來實(shí)現(xiàn)調(diào)用下一個(gè)閥的invoke方法。然后簡單的打印了請(qǐng)求的ip地址。

最后再看 StandardPipelineValveContext的invokeNext方法,調(diào)用完普通閥數(shù)組valves的閥后,開始調(diào)用基礎(chǔ)閥basic的 invoke方法,這里先說基礎(chǔ)閥的初始化,在每一個(gè)容器的構(gòu)造函數(shù)類就已經(jīng)初始化了基礎(chǔ)閥,看容器StandardEngine的構(gòu)造函數(shù):

  1. public ?StandardEngine()?{??
  2. ??????? super ();??
  3. ???????pipeline.setBasic( new ?StandardEngineValve()); //容器StandardEngine的基礎(chǔ)閥StandardEngineValve ??
  4. ???}??
即在容器構(gòu)造的時(shí)候就已經(jīng)把基礎(chǔ)閥添加進(jìn)管道pipeline中,這樣在 StandardPipelineValveContext中的invokeNext方法里就能調(diào)用 基礎(chǔ)閥的invoke了,當(dāng)basic.invoke(request, response, this);進(jìn)入基礎(chǔ)閥StandardEngineValve,看基礎(chǔ)閥 StandardEngineValve的invoke方法:

  1. public ? void ?invoke(Request?request,?Response?response,??
  2. ????????????????????ValveContext?valveContext)??
  3. ????? throws ?IOException,?ServletException?{??
  4. ????...........................??
  5. ??
  6. ????? //?Ask?this?Host?to?process?this?request ??
  7. ?????host.invoke(request,?response);??
  8. ??
  9. ?}??

這里省略了很多代碼,主要是為了更加理解調(diào)用邏輯,在StandardEngine的基礎(chǔ)閥StandardEngineValve里,調(diào)用了子容器invoke方法(這里子容器就是StandardHost), 還記得一開始connector.invoke(request, response)(即StandardEngine的invoke方法)現(xiàn)在順利的傳遞到子容器StandardHost的invoke方法,變成了StandardHost.invoke(request, response)。 由此可以猜測(cè)StandardHost也會(huì)傳遞給它的子容器,最后傳遞到最小的容器StandardWrapper的invoke方法,然后調(diào)用StandardWrapper的基礎(chǔ)閥StandardWrapperValue的invoke方法,由于StandardWrapper是最小的容器了,不能再傳遞到其他容器的invoke方法了,那它的invoke方法做了什么?主要做了兩件事, 1:創(chuàng)建一個(gè)過濾器鏈并 ?2:分配一個(gè)servlet或者jsp,主要代碼如下:

  1. StandardWrapperValue的invoke方法??
  2. ????????????servlet?=?wrapper.allocate();?? //分配一個(gè)servlet ??
  3. ....................................................................??
  4. ????????? //?Create?the?filter?chain?for?this?request ??
  5. ????????????ApplicationFilterChain?filterChain?=??
  6. ????????????createFilterChain(request,?servlet);??
  7. .........................................................??
  8. ????????????String?jspFile?=?wrapper.getJspFile(); //分配一個(gè)jsp ??
  9. ???????????? if ?(jspFile?!=? null )??
  10. ????????????????sreq.setAttribute(Globals.JSP_FILE_ATTR,?jspFile);??
  11. ???????????? else ??
  12. ????????????????sreq.removeAttribute(Globals.JSP_FILE_ATTR);??
  13. ???????????? if ?((servlet?!=? null )?&&?(filterChain?!=? null ))?{??
  14. ????????????????filterChain.doFilter(sreq,?sres); //調(diào)用過濾器鏈處理請(qǐng)求,sreq和sres是request和response的包裝類,在這里面會(huì)調(diào)用servlet的services方法 ??
  15. ????????????}??

這里先不關(guān)注jsp,只關(guān)注一下servlet,通過servlet = wrapper.allocate(); 進(jìn)入StandardWrapper的allocate方法,allocate主要就是調(diào)用了loadServlet方法,在loadServlet方法類用tomcat自己的類加載器實(shí)例化了一個(gè)servlet對(duì)象,并調(diào)用了該servlet的init和service方法:

  1. StandardWrapper的loadServlet方法(這里省略了很多其他的代碼)??
  2. ?Servlet?servlet?=? null ;??
  3. String?actualClass?=?servletClass; //servlet的字節(jié)碼字符串 ??
  4. ?Loader?loader?=?getLoader();??
  5. ClassLoader?classLoader?=?loader.getClassLoader(); //得到類加載器 ??
  6. ?Class?classClass?=? null ;??
  7. ???????????????? if ?(classLoader?!=? null )?{??
  8. ????????????????????System.out.println( "Using?classLoader.loadClass" );??
  9. ????????????????????classClass?=?classLoader.loadClass(actualClass); //通過類加載器實(shí)例化servlet ??
  10. ????????????????}? else ?{??
  11. ????????????????????System.out.println( "Using?forName" );??
  12. ????????????????????classClass?=?Class.forName(actualClass); //通過反射實(shí)例化servlet ??
  13. ????????????????}??
  14. ?servlet?=?(Servlet)?classClass.newInstance(); //實(shí)例化servlet ??
  15. ??servlet.init(facade); //調(diào)用servlet的init ??
  16. ????? if ?((loadOnStartup?>? 0 )?&&?(jspFile?!=? null ))?{??
  17. ???????????????????? //?Invoking?jspInit ??
  18. ????????????????????HttpRequestBase?req?=? new ?HttpRequestBase();??
  19. ????????????????????HttpResponseBase?res?=? new ?HttpResponseBase();??
  20. ????????????????????req.setServletPath(jspFile);??
  21. ????????????????????req.setQueryString( "jsp_precompile=true" );??
  22. ????????????????????servlet.service(req,?res);??
  23. ????????????????}; //調(diào)用jsp的service方法,jsp會(huì)被編譯成servlet,所以也會(huì)有service方法 ??

至此已經(jīng)把請(qǐng)求傳遞到servlet的service(或者jsp的service)方法,整個(gè)處理請(qǐng)求到這里就結(jié)束了,剩下的就是返回客戶端了。這里提出幾個(gè)問題。 1:那么多的servlet,tomcat是怎么知道要請(qǐng)求到哪個(gè)servlet? 這個(gè)問題留待下篇博客再來講吧。

Tomcat源碼分析(四)--容器處理鏈接之責(zé)任鏈模式


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 久久这里只有精品免费视频 | 精品视频免费在线 | 久久精品国产福利 | 美女又黄又免费的视频 | 欧美日韩一区二区综合在线视频 | 国产99久久亚洲综合精品 | 国产一级持黄大片99久久 | 在线se| 久草在线视频首页 | 日本四虎影视 | 99精品久久久久久久 | 久久精品国产精品亚洲20 | 中文字幕精品一区二区三区视频 | 日韩视频一区二区 | 国产亚洲精品自在久久77 | 久久青草免费免费91线频观看 | 一区二区三区免费视频网站 | 91久久精品国产亚洲 | 青青青青啪视频在线观看 | 中文字幕日韩国产 | 国产精品一区在线麻豆 | 99视频免费在线观看 | 伊人久久一本大道 | 欧美在线激情视频 | 欧美啪啪小视频 | 九九精品免视频国产成人 | 在线观看精品国内福利视频 | 日本四虎影院 | 亚洲精品一区二区三区香蕉在线看 | 国产网址| 日本福利片国产午夜久久 | 四虎影视1515hh四虎免费 | 波多野吉衣一区二区三区四区 | 中文在线免费不卡视频 | 夜夜骑加勒比 | 久久综合九色综合国产 | 欧美乱大交xxxxx另类 | 亚洲精品久久玖玖玖玖 | 亚洲一区二区三区在线免费观看 | 日日狠狠的日日日日 | 国产一级特黄一级毛片 |