Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

深入淺出 Web 容器 - Tomcat 原始碼分析

3,262 views

Published on

2009 Java Certification Day

Published in: Technology
  • Be the first to comment

深入淺出 Web 容器 - Tomcat 原始碼分析

  1. 1. 深入淺出 Web 容器 Tomcat 原始碼分析林信良資深技術顧問http://openhome.cccaterpillar@openhome.cc
  2. 2. 主題• Web 容器與 Servlet• 從 HTTP 請求到 service()• 在service()的前後• 從 JSP 到 Servlet• 自訂標籤處理
  3. 3. Web 容器與 Servlet
  4. 4. Web 容器與 Servlet容器(Container)…裝水的嗎? 別鬧了…容器是個 Java 應用程式, 介於 Servlet 與Web 伺服器之間,我 管很多事,你不用認識 Web 伺服器, 你只要認得我!
  5. 5. Web 容器與 Servlet 管很多事?哪些事啊? 你沒想過 HTTP 那些麻煩的 東西是怎麼變成 Java 物件的 嗎?你以為 Servlet 中的 Request 與 Response 是怎 麼來的?
  6. 6. Web 容器與 Servlet 看起來好像很複雜? 要了解我的內在確實不容易, 那…先從簡單的開始好了…
  7. 7. 知道一個Servlet是實作 Servlet 介面嗎? …..
  8. 8. 來個簡化版的…
  9. 9. RequestFacade requestFacade =if(servlets.get(servletName) == null) { new RequestFacade(request); servlet = (Servlet) myClass.newInstance(); ResponseFacade responseFacade = servlets.put(servletName, servlet); new ResponseFacade(response); servlet.init(new Config(myClass)); } else { servlet = servlets.get(servletName); } servlet.service( (ServletRequest) requestFacade, (ServletResponse) responseFacade);public void destroy() { for(Servlet servlet : servlets.values()) { servlet.destroy(); }} 至少你要知道init()、 service()與destroy()…
  10. 10. Web 容器與 Servlet 你管的就是這些? 當然更多!不只ServletRequest、 ServletResponse、 ServletConfig、Servlet這些… 對了!這個範例改寫自這邊… http://onjava.com/pub/a/onjava/2003/05/14/j ava_webserver.html
  11. 11. 從 HTTP 請求到 service() 我實際上很強壯的 啦….XD
  12. 12. 從 HTTP 請求到 service() 當請求來到時是 Worker Thread 模式
  13. 13. public void run() { while (running) { 採用Thread Pool...XD // Allocate a new worker thread MasterSlaveWorkerThread workerThread = createWorkerThread(); // Accept the next incoming connection from the server socket ... Socket socket = acceptSocket(); workerThread.assign(socket); } ..} 沒原始碼沒真相...XD
  14. 14. 從 HTTP 請求到 service()接下來快轉一下…來到了Http11Processor… 我應該說過我負責建立Request與 Response物件吧…這個類別剖析HTTP 並設定Request、Response
  15. 15. 從 HTTP 請求到 service()在Http11Processor的process()中,呼叫 adapter.service()adapter.service(request, response);再來的話這邊的request、response會被org.apache.catalina.connector套件中的Request、Response包裹起來... WHY?
  16. 16. 從 HTTP 請求到 service()package org.apache.catalina.connector;public class Request implements HttpServletRequest { …}package org.apache.catalina.connector;public class Response implements HttpServletResponse { …}
  17. 17. 從 HTTP 請求到 service()再快轉一下…Request與Response送到容器ContainerBase,最後來到StandardWrapper…public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {你要注意一下loadServlet()方法…回憶…•Servlet第一次被請求時會被載入…•建立ServletConfig…•執行init()…•呼叫service()…
  18. 18. if (classLoader != null) { classClass = classLoader.loadClass(actualClass); } else { classClass = Class.forName(actualClass); } } ...// Instantiate and initialize an instance of the servlet classtry { servlet = (Servlet) classClass.newInstance();}...try { … servlet.init(facade); … servlet.service(req, res);}facade 參考至實作 ServletConfig 的實例protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
  19. 19. 從 HTTP 請求到 service() SingleThreadModel 怎麼實現?你 可以自己找找看… 喵…好複雜…Orz已經省略了很多細節了…你可以用這個流程來研究一些更深入的…XD
  20. 20. 在service()的前後
  21. 21. 在service()的前後你知道在Servlet前可以套用Filter吧!… 好像是有這麼一回事…XD
  22. 22. 實現了Interceptor Filter模式
  23. 23. 當我們又來到Servlet…
  24. 24. 在service()的前後• GenericServlet類別 – 還實作了ServletConfig介面,將容器呼叫 init()方法時所傳入的ServletConfig實例 封裝起來 – service()方法直接標示為abstract而沒有 任何的實作• HTTP相關服務流程定義在HttpServlet 的service()方法
  25. 25. 在service()的前後 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); // 取得請求的方法 if (method.equals(METHOD_GET)) { // HTTP GET // 略... doGet(req, resp); // 略 ... } else if (method.equals(METHOD_HEAD)) { // HTTP HEADprotected void doGet(HttpServletRequest req, HttpServletResponse resp) // 略 ... throws ServletException, resp); doHead(req, IOException { String protocol if req.getProtocol(); } else = (method.equals(METHOD_POST)) { // HTTP POST String msg = lStrings.getString("http.method_get_not_supported"); // 略 ... if (protocol.endsWith("1.1")) { doPost(req, resp); resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else if (method.equals(METHOD_PUT)) { // HTTP PUT } else { // 略 ... resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }}
  26. 26. 在service()的前後 這實現了Template Method模式。。。現在好像談過了幾個模式?WorkerThread、Interceptor Filter、TemplateMethod…
  27. 27. 在service()的前後 來談一下Session怎麼建好了…知道 Session預設用Cookie儲存Session Id 吧… 餅乾我知道…
  28. 28. 在service()的前後if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) { session = manager.createSession(getRequestedSessionId());} else { session = manager.createSession(null);}public Session createSession(String sessionId) { Session session = createEmptySession() session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); if (sessionId == null) { sessionId = generateSessionId(); session.setId(sessionId); sessionCounter++; return (session);}
  29. 29. 在service()的前後 Servlet 談好久了…要來談一 下 JSP 嗎? …XD 你應該看一下org.apache.catalina. session.StandardSession,了解 Session怎麼儲存…好吧!接下來換個口味好 了…
  30. 30. 從 JSP 到 Servlet 跟你下棋的其實是一 隻貓…@.@
  31. 31. 從 JSP 到 Servlet 使用JSP不是比較簡單嗎? … 在我的世界中真正服務的只有Servlet…沒有 JSP…
  32. 32. 從 JSP 到 Servlet<%@page contentType="text/html" pageEncoding="UTF-8"%><html><head> <title>SimpleJSP</title></head><body> <h1><%= new java.util.Date() %></h1></body></html>
  33. 33. 從 JSP 到 Servletpackage org.apache.jsp;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { // 略... public void _jspInit() { // 略... } public void _jspDestroy() { } public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { // 略... }}
  34. 34. 從 JSP 到 Servlet
  35. 35. 從 JSP 到 Servletpublic abstract class HttpJspBase extends HttpServlet implements HttpJspPage { // 略... public final void init(ServletConfig config) throws ServletException { super.init(config); jspInit(); _jspInit(); } // 略... public final void destroy() { jspDestroy(); _jspDestroy(); } public final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { _jspService(request, response); } // 略...}
  36. 36. 從 JSP 到 Servlet• 在<%!與%>之間宣告的程式碼,都將轉譯 為Servlet中的類別成員或方法• <%與%>之間所包括的內容,將被轉譯為 Servlet原始碼_jspService()方法中的內 容• <%=與%>運算式元素中的運算式,會直接 轉譯為out物件print()輸出時的指定內 容
  37. 37. 從 JSP 到 Servlet • 隱含物件,其實就是_jspService()中的 區域參考名稱…application = pageContext.getServletContext();config = pageContext.getServletConfig();session = pageContext.getSession();out = pageContext.getOut(); 這麼說Servlet作的到的,JSP 都作的到 … 原則上是…因為都是轉成Servlet…只不過角色 職責不同…這是另一個故事了…
  38. 38. 自訂標籤處理我討厭義大利麵...>.<
  39. 39. 自訂標籤處理 聽說使用 JSTL 就不用吃義大利麵 … 那是新的 HTML 標籤嗎?。。XD 別傻了...我只處理 Java 的東西…每個標籤 後面都還是 Java 物件在處理事情。。XD
  40. 40. 自訂標籤處理
  41. 41. 自訂標籤處理• 當JSP網頁中包括Simple Tag自訂標籤 ,在轉 譯之後... 1. 建立自訂標籤處理器實例。 2. 呼叫標籤處理器的setJspContext()方法設定 PageContext實例。 3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理 器的setParent()方法,並傳入外層標籤處理器的 實例。 4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的 setTest()方法來設定)。 5. 呼叫標籤處理器的setJspBody()方法設定 JspFragment實例。 6. 呼叫標籤處理器的doTag()方法。 7. 銷毀標籤處理器實例。
  42. 42. <f:choose> <f:when test="${user.valid}"> <h1>${user.name}登入成功</h1> </f:when> ..</f:choose>cc.openhome.WhenTag _jspx_th_f_005fwhen_005f0 = new cc.openhome.WhenTag();..._jspx_th_f_005fwhen_005f0.setJspContext(_jspx_page_context);_jspx_th_f_005fwhen_005f0.setParent(_jspx_parent);…_jspx_th_f_005fwhen_005f0.setTest(…);_jspx_th_f_005fwhen_005f0.setJspBody(new Helper(…));_jspx_th_f_005fwhen_005f0.doTag();… 這代表用Simple Tag實作標籤時,你的標籤處 理器不能太肥大…
  43. 43. 自訂標籤處理• 當JSP中遇到TagSupport自訂標籤時 1. 嘗試從標籤池(Tag Pool)找到可用的標籤物件,如果找到就 直接使用,如果沒找到就建立新的標籤物件。 2. 呼叫標籤處理器的setPageContext()方法設定 PageContext實例。 3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理器的 setParent()方法,並傳入外層標籤處理器的實例。 4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的setTest()方 法來設定)。 5. 呼叫標籤處理器的doStartTag()方法,並依不同的傳回值決 定是否執行本體或呼叫doAfterBody()、doEndTag()方法。 6. 將標籤處理器實例置入標籤池中以便再度使用。
  44. 44. 自訂標籤處理org.apache.taglibs.standard.tag.rt.core.WhenTag _jspx_th_c_005fwhen_005f0 = (org.apache.taglibs.standard.tag.rt.core.WhenTag) _005fjspx_005ftagPool_005fc_005fwhen_0026_005ftest.get( org.apache.taglibs.standard.tag.rt.core.WhenTag.class);..._005fjspx_005ftagPool_005fc_005fwhen_0026_005ftest.reuse( _jspx_th_c_005fwhen_005f0); 這代表用TagSupport實作標籤時,必須注意 是否要重置標籤處理器的狀態。。 Pool的工作是由org.apache.jasper. runtime.TagHandlerPool完成…
  45. 45. 自訂標籤處理public Tag get(Class handlerClass) throws JspException { Tag handler = null; synchronized( this ) { if (current >= 0) { handler = handlers[current--]; return handler; } } try { Tag instance = (Tag) handlerClass.newInstance(); …}public void reuse(Tag handler) { synchronized( this ) { if (current < (handlers.length - 1)) { handlers[++current] = handler; return; } } …}
  46. 46. 自訂標籤處理 我記得還有個 Tag File 的東西… 是啦!不過…Tag File 會被我轉成 Simple Tag 的實作…XDpackage org.apache.jsp.tag.web;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;public final class Errors_tag extends javax.servlet.jsp.tagext.SimpleTagSupport implements org.apache.jasper.runtime.JspSourceDependent {
  47. 47. 最後…我為什麼要了解這麼多細節…因為我代勞太多事了…你以為有些行為很奇怪…但其實看一下原始碼就什麼都知道了…
  48. 48. Thanks林信良 http://openhome.cc caterpillar@openhome.cc 52

×