SlideShare ist ein Scribd-Unternehmen logo
1 von 52
Downloaden Sie, um offline zu lesen
深入淺出 Web 容器
    Tomcat 原始碼分析
林信良
資深技術顧問
http://openhome.cc
caterpillar@openhome.cc
主題
• Web 容器與 Servlet
• 從 HTTP 請求到 service()
• 在service()的前後
• 從 JSP 到 Servlet
• 自訂標籤處理
Web 容器與 Servlet
Web 容器與 Servlet

容器(Container)…裝水的嗎?


     別鬧了…容器是個 Java 應用程式,
     介於 Servlet 與Web 伺服器之間,我
     管很多事,你不用認識 Web 伺服器,
     你只要認得我!
Web 容器與 Servlet
    管很多事?哪些事啊?




  你沒想過 HTTP 那些麻煩的
  東西是怎麼變成 Java 物件的
  嗎?你以為 Servlet 中的
  Request 與 Response 是怎
  麼來的?
Web 容器與 Servlet
     看起來好像很複雜?


      要了解我的內在確實不容易,
      那…先從簡單的開始好了…
知道一個Servlet是實
作 Servlet 介面嗎?   …..
來個簡化版的…
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()…
Web 容器與 Servlet
       你管的就是這些?

        當然更多!不只ServletRequest、
        ServletResponse、
        ServletConfig、Servlet這些…

 對了!這個範例改寫自這邊…
  http://onjava.com/pub/a/onjava/2003/05/14/j
  ava_webserver.html
從 HTTP 請求到 service()
            我實際上很強壯的
            啦….XD
從 HTTP 請求到 service()
     當請求來到時是 Worker Thread 模式
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
從 HTTP 請求到 service()
接下來快轉一下…來到了Http11Processor…




     我應該說過我負責建立Request與
     Response物件吧…這個類別剖析HTTP
     並設定Request、Response
從 HTTP 請求到 service()
在Http11Processor的process()中,
呼叫 adapter.service()
adapter.service(request, response);


再來的話這邊的request、response會被
org.apache.catalina.connector套
件中的Request、Response包裹起來...


                  WHY?
從 HTTP 請求到 service()
package org.apache.catalina.connector;
public class Request implements HttpServletRequest {
    …
}

package org.apache.catalina.connector;
public class Response implements HttpServletResponse {
    …
}
從 HTTP 請求到 service()
再快轉一下…Request與Response送到容器
ContainerBase,最後來到StandardWrapper…
public class StandardWrapper extends ContainerBase
              implements ServletConfig, Wrapper, NotificationEmitter {



你要注意一下loadServlet()方法…
回憶…
•Servlet第一次被請求時會被載入…
•建立ServletConfig…
•執行init()…
•呼叫service()…
if (classLoader != null) {
            classClass = classLoader.loadClass(actualClass);
        } else {
            classClass = Class.forName(actualClass);
        }
    }
    ...
// Instantiate and initialize an instance of the servlet class
try {
    servlet = (Servlet) classClass.newInstance();
}
...
try {
    …
        servlet.init(facade);
    …
        servlet.service(req, res);
}

facade 參考至實作 ServletConfig 的實例

protected StandardWrapperFacade facade =
       new StandardWrapperFacade(this);
從 HTTP 請求到 service()
    SingleThreadModel 怎麼實現?你
    可以自己找找看…
            喵…好複雜…Orz

已經省略了很多細節了…你可以用這
個流程來研究一些更深入的…XD
在service()的前後
在service()的前後
你知道在Servlet前可以套用Filter
吧!…




      好像是有這麼一回事…XD
實現了Interceptor Filter模式
當我們又來到Servlet…
在service()的前後
• GenericServlet類別
 – 還實作了ServletConfig介面,將容器呼叫
   init()方法時所傳入的ServletConfig實例
   封裝起來
 – service()方法直接標示為abstract而沒有
   任何的實作
• HTTP相關服務流程定義在HttpServlet
  的service()方法
在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 HEAD
protected 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);
        }
    }
}
在service()的前後

     這實現了Template Method模式。。。


現在好像談過了幾個模式?Worker
Thread、Interceptor Filter、Template
Method…
在service()的前後
   來談一下Session怎麼建好了…知道
   Session預設用Cookie儲存Session Id
   吧…
               餅乾我知道…
在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);
}
在service()的前後
    Servlet 談好久了…要來談一
    下 JSP 嗎? …XD


   你應該看一下org.apache.catalina.
   session.StandardSession,了解
   Session怎麼儲存…好吧!接下來換個口味好
   了…
從 JSP 到 Servlet
            跟你下棋的其實是一
            隻貓…@.@
從 JSP 到 Servlet
     使用JSP不是比較簡單嗎? …

    在我的世界中真正服務的只有Servlet…沒有
    JSP…
從 JSP 到 Servlet
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
    <title>SimpleJSP</title>
</head>
<body>
    <h1><%= new java.util.Date() %></h1>
</body>
</html>
從 JSP 到 Servlet
package 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 {
         // 略...
  }
}
從 JSP 到 Servlet
從 JSP 到 Servlet
public 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);
    }
    // 略...
}
從 JSP 到 Servlet
• 在<%!與%>之間宣告的程式碼,都將轉譯
  為Servlet中的類別成員或方法
• <%與%>之間所包括的內容,將被轉譯為
  Servlet原始碼_jspService()方法中的內
  容
• <%=與%>運算式元素中的運算式,會直接
  轉譯為out物件print()輸出時的指定內
  容
從 JSP 到 Servlet
 • 隱含物件,其實就是_jspService()中的
   區域參考名稱…
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();



                  這麼說Servlet作的到的,JSP
                  都作的到 …
             原則上是…因為都是轉成Servlet…只不過角色
             職責不同…這是另一個故事了…
自訂標籤處理
我討厭義大利
麵...>.<
自訂標籤處理
 聽說使用 JSTL 就不用吃義大利麵 …
 那是新的 HTML 標籤嗎?。。XD

   別傻了...我只處理 Java 的東西…每個標籤
   後面都還是 Java 物件在處理事情。。XD
自訂標籤處理
自訂標籤處理
•   當JSP網頁中包括Simple Tag自訂標籤 ,在轉
    譯之後...
    1. 建立自訂標籤處理器實例。
    2. 呼叫標籤處理器的setJspContext()方法設定
       PageContext實例。
    3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理
       器的setParent()方法,並傳入外層標籤處理器的
       實例。
    4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的
       setTest()方法來設定)。
    5. 呼叫標籤處理器的setJspBody()方法設定
       JspFragment實例。
    6. 呼叫標籤處理器的doTag()方法。
    7. 銷毀標籤處理器實例。
<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實作標籤時,你的標籤處
              理器不能太肥大…
自訂標籤處理
•   當JSP中遇到TagSupport自訂標籤時
    1. 嘗試從標籤池(Tag Pool)找到可用的標籤物件,如果找到就
       直接使用,如果沒找到就建立新的標籤物件。
    2. 呼叫標籤處理器的setPageContext()方法設定
       PageContext實例。
    3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理器的
       setParent()方法,並傳入外層標籤處理器的實例。
    4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的setTest()方
       法來設定)。
    5. 呼叫標籤處理器的doStartTag()方法,並依不同的傳回值決
       定是否執行本體或呼叫doAfterBody()、doEndTag()方法。
    6. 將標籤處理器實例置入標籤池中以便再度使用。
自訂標籤處理
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完成…
自訂標籤處理
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;
        }
    }
    …
}
自訂標籤處理
               我記得還有個 Tag File 的東西…


              是啦!不過…Tag File 會被我轉成 Simple
              Tag 的實作…XD

package 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 {
最後…
我為什麼要了解這麼多細節…



因為我代勞太多事了…你以為有些行為很奇
怪…但其實看一下原始碼就什麼都知道了…
Thanks
林信良 http://openhome.cc
    caterpillar@openhome.cc




                              52

Weitere ähnliche Inhalte

Was ist angesagt?

Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 ServletServlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
Justin Lin
 

Was ist angesagt? (20)

從SOA到REST -- Web Service、WCF、WebAPI的應用情境
從SOA到REST -- Web Service、WCF、WebAPI的應用情境從SOA到REST -- Web Service、WCF、WebAPI的應用情境
從SOA到REST -- Web Service、WCF、WebAPI的應用情境
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBoot
 
JIT のコードを読んでみた
JIT のコードを読んでみたJIT のコードを読んでみた
JIT のコードを読んでみた
 
Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門Spring 2.0 技術手冊第二章 - Spring 入門
Spring 2.0 技術手冊第二章 - Spring 入門
 
Spring Framework - Data Access
Spring Framework - Data AccessSpring Framework - Data Access
Spring Framework - Data Access
 
Rust と Wasmの現実
Rust と Wasmの現実Rust と Wasmの現実
Rust と Wasmの現実
 
Net 6 的 blazor 開發新視界
Net 6 的 blazor 開發新視界Net 6 的 blazor 開發新視界
Net 6 的 blazor 開發新視界
 
RESTful API Design
RESTful API DesignRESTful API Design
RESTful API Design
 
Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 
PHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 とPHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 と
 
Spring MVC
Spring MVCSpring MVC
Spring MVC
 
これからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきことこれからSpringを使う開発者が知っておくべきこと
これからSpringを使う開発者が知っておくべきこと
 
Spring Framework - Spring Security
Spring Framework - Spring SecuritySpring Framework - Spring Security
Spring Framework - Spring Security
 
Spring Framework - Core
Spring Framework - CoreSpring Framework - Core
Spring Framework - Core
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 ServletServlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
Servlet & JSP 教學手冊第二版試讀 - 撰寫與設定 Servlet
 
Spring Boot and REST API
Spring Boot and REST APISpring Boot and REST API
Spring Boot and REST API
 
Spring 2.0 技術手冊第五章 - JDBC、交易支援
Spring 2.0 技術手冊第五章 - JDBC、交易支援Spring 2.0 技術手冊第五章 - JDBC、交易支援
Spring 2.0 技術手冊第五章 - JDBC、交易支援
 
A New CLI for Spring Developer Productivity
A New CLI for Spring Developer ProductivityA New CLI for Spring Developer Productivity
A New CLI for Spring Developer Productivity
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 

Andere mochten auch (6)

讓程式展現樂趣 玩出實驗精神與創造力
讓程式展現樂趣 玩出實驗精神與創造力讓程式展現樂趣 玩出實驗精神與創造力
讓程式展現樂趣 玩出實驗精神與創造力
 
Java 8 與 retrolambda
Java 8 與 retrolambdaJava 8 與 retrolambda
Java 8 與 retrolambda
 
Arduino、Web 到 IoT
Arduino、Web 到 IoTArduino、Web 到 IoT
Arduino、Web 到 IoT
 
OpenSCAD Workshop
OpenSCAD WorkshopOpenSCAD Workshop
OpenSCAD Workshop
 
3D 之邏輯與美感交會 - OpenSCAD
3D 之邏輯與美感交會 - OpenSCAD3D 之邏輯與美感交會 - OpenSCAD
3D 之邏輯與美感交會 - OpenSCAD
 
網站系統安全及資料保護設計認知
網站系統安全及資料保護設計認知網站系統安全及資料保護設計認知
網站系統安全及資料保護設計認知
 

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

Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
yiditushe
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记
yiditushe
 
J2ee经典学习笔记
J2ee经典学习笔记J2ee经典学习笔记
J2ee经典学习笔记
yiditushe
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识
yiditushe
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
keelii
 
高性能并发Web服务器实现核心内幕
高性能并发Web服务器实现核心内幕高性能并发Web服务器实现核心内幕
高性能并发Web服务器实现核心内幕
ideawu
 
香港六合彩
香港六合彩香港六合彩
香港六合彩
香港六合彩香港六合彩
香港六合彩
aaveow
 
Node.js开发体验
Node.js开发体验Node.js开发体验
Node.js开发体验
QLeelulu
 
jsp基础速成精华讲解
jsp基础速成精华讲解jsp基础速成精华讲解
jsp基础速成精华讲解
wensheng wei
 
Jdbc4 0 规范技术预研
Jdbc4 0 规范技术预研Jdbc4 0 规范技术预研
Jdbc4 0 规范技术预研
lorisjand
 
线程编程方面
线程编程方面线程编程方面
线程编程方面
yiditushe
 
Jsp面试知识
Jsp面试知识Jsp面试知识
Jsp面试知识
yiditushe
 
Struts学习笔记
Struts学习笔记Struts学习笔记
Struts学习笔记
yiditushe
 

Ähnlich wie 深入淺出 Web 容器 - Tomcat 原始碼分析 (20)

Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
 
用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Services用JAX-RS和Jersey完成RESTful Web Services
用JAX-RS和Jersey完成RESTful Web Services
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记
 
Jsp
JspJsp
Jsp
 
J2ee经典学习笔记
J2ee经典学习笔记J2ee经典学习笔记
J2ee经典学习笔记
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
 
高性能并发Web服务器实现核心内幕
高性能并发Web服务器实现核心内幕高性能并发Web服务器实现核心内幕
高性能并发Web服务器实现核心内幕
 
香港六合彩
香港六合彩香港六合彩
香港六合彩
 
ev2oik
ev2oikev2oik
ev2oik
 
香港六合彩
香港六合彩香港六合彩
香港六合彩
 
Node.js开发体验
Node.js开发体验Node.js开发体验
Node.js开发体验
 
jsp基础速成精华讲解
jsp基础速成精华讲解jsp基础速成精华讲解
jsp基础速成精华讲解
 
千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7
 
Jdbc4 0 规范技术预研
Jdbc4 0 规范技术预研Jdbc4 0 规范技术预研
Jdbc4 0 规范技术预研
 
线程编程方面
线程编程方面线程编程方面
线程编程方面
 
Jsp面试知识
Jsp面试知识Jsp面试知识
Jsp面试知识
 
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
Struts学习笔记
Struts学习笔记Struts学习笔记
Struts学习笔记
 

Mehr von Justin Lin

Mehr von Justin Lin (20)

Ch14 簡介 Spring Boot
Ch14 簡介 Spring BootCh14 簡介 Spring Boot
Ch14 簡介 Spring Boot
 
Ch13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/SecurityCh13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/Security
 
Ch12 Spring 起步走
Ch12 Spring 起步走Ch12 Spring 起步走
Ch12 Spring 起步走
 
Ch11 簡介 JavaMail
Ch11 簡介 JavaMailCh11 簡介 JavaMail
Ch11 簡介 JavaMail
 
Ch10 Web 容器安全管理
Ch10 Web 容器安全管理Ch10 Web 容器安全管理
Ch10 Web 容器安全管理
 
Ch09 整合資料庫
Ch09 整合資料庫Ch09 整合資料庫
Ch09 整合資料庫
 
Ch08 自訂標籤
Ch08 自訂標籤Ch08 自訂標籤
Ch08 自訂標籤
 
Ch07 使用 JSTL
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTL
 
Ch06 使用 JSP
Ch06 使用 JSPCh06 使用 JSP
Ch06 使用 JSP
 
Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器Ch05 Servlet 進階 API、過濾器與傾聽器
Ch05 Servlet 進階 API、過濾器與傾聽器
 
Ch04 會話管理
Ch04 會話管理Ch04 會話管理
Ch04 會話管理
 
Ch03 請求與回應
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應
 
Ch02 撰寫與設定 Servlet
Ch02 撰寫與設定 ServletCh02 撰寫與設定 Servlet
Ch02 撰寫與設定 Servlet
 
CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式
 
14. 進階主題
14. 進階主題14. 進階主題
14. 進階主題
 
13.並行、平行與非同步
13.並行、平行與非同步13.並行、平行與非同步
13.並行、平行與非同步
 
12. 除錯、測試與效能
12. 除錯、測試與效能12. 除錯、測試與效能
12. 除錯、測試與效能
 
11. 常用內建模組
11. 常用內建模組11. 常用內建模組
11. 常用內建模組
 
10. 資料永續與交換
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換
 
9. 資料結構
9. 資料結構9. 資料結構
9. 資料結構
 

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

  • 1. 深入淺出 Web 容器 Tomcat 原始碼分析 林信良 資深技術顧問 http://openhome.cc caterpillar@openhome.cc
  • 2. 主題 • Web 容器與 Servlet • 從 HTTP 請求到 service() • 在service()的前後 • 從 JSP 到 Servlet • 自訂標籤處理
  • 4. Web 容器與 Servlet 容器(Container)…裝水的嗎? 別鬧了…容器是個 Java 應用程式, 介於 Servlet 與Web 伺服器之間,我 管很多事,你不用認識 Web 伺服器, 你只要認得我!
  • 5. Web 容器與 Servlet 管很多事?哪些事啊? 你沒想過 HTTP 那些麻煩的 東西是怎麼變成 Java 物件的 嗎?你以為 Servlet 中的 Request 與 Response 是怎 麼來的?
  • 6. Web 容器與 Servlet 看起來好像很複雜? 要了解我的內在確實不容易, 那…先從簡單的開始好了…
  • 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. Web 容器與 Servlet 你管的就是這些? 當然更多!不只ServletRequest、 ServletResponse、 ServletConfig、Servlet這些… 對了!這個範例改寫自這邊… http://onjava.com/pub/a/onjava/2003/05/14/j ava_webserver.html
  • 11. 從 HTTP 請求到 service() 我實際上很強壯的 啦….XD
  • 12. 從 HTTP 請求到 service() 當請求來到時是 Worker Thread 模式
  • 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. 從 HTTP 請求到 service() 接下來快轉一下…來到了Http11Processor… 我應該說過我負責建立Request與 Response物件吧…這個類別剖析HTTP 並設定Request、Response
  • 15. 從 HTTP 請求到 service() 在Http11Processor的process()中, 呼叫 adapter.service() adapter.service(request, response); 再來的話這邊的request、response會被 org.apache.catalina.connector套 件中的Request、Response包裹起來... WHY?
  • 16. 從 HTTP 請求到 service() package org.apache.catalina.connector; public class Request implements HttpServletRequest { … } package org.apache.catalina.connector; public class Response implements HttpServletResponse { … }
  • 17. 從 HTTP 請求到 service() 再快轉一下…Request與Response送到容器 ContainerBase,最後來到StandardWrapper… public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { 你要注意一下loadServlet()方法… 回憶… •Servlet第一次被請求時會被載入… •建立ServletConfig… •執行init()… •呼叫service()…
  • 18. if (classLoader != null) { classClass = classLoader.loadClass(actualClass); } else { classClass = Class.forName(actualClass); } } ... // Instantiate and initialize an instance of the servlet class try { servlet = (Servlet) classClass.newInstance(); } ... try { … servlet.init(facade); … servlet.service(req, res); } facade 參考至實作 ServletConfig 的實例 protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
  • 19. 從 HTTP 請求到 service() SingleThreadModel 怎麼實現?你 可以自己找找看… 喵…好複雜…Orz 已經省略了很多細節了…你可以用這 個流程來研究一些更深入的…XD
  • 22.
  • 24.
  • 25.
  • 27. 在service()的前後 • GenericServlet類別 – 還實作了ServletConfig介面,將容器呼叫 init()方法時所傳入的ServletConfig實例 封裝起來 – service()方法直接標示為abstract而沒有 任何的實作 • HTTP相關服務流程定義在HttpServlet 的service()方法
  • 28. 在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 HEAD protected 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); } } }
  • 29. 在service()的前後 這實現了Template Method模式。。。 現在好像談過了幾個模式?Worker Thread、Interceptor Filter、Template Method…
  • 30. 在service()的前後 來談一下Session怎麼建好了…知道 Session預設用Cookie儲存Session Id 吧… 餅乾我知道…
  • 31. 在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); }
  • 32. 在service()的前後 Servlet 談好久了…要來談一 下 JSP 嗎? …XD 你應該看一下org.apache.catalina. session.StandardSession,了解 Session怎麼儲存…好吧!接下來換個口味好 了…
  • 33. 從 JSP 到 Servlet 跟你下棋的其實是一 隻貓…@.@
  • 34. 從 JSP 到 Servlet 使用JSP不是比較簡單嗎? … 在我的世界中真正服務的只有Servlet…沒有 JSP…
  • 35. 從 JSP 到 Servlet <%@page contentType="text/html" pageEncoding="UTF-8"%> <html> <head> <title>SimpleJSP</title> </head> <body> <h1><%= new java.util.Date() %></h1> </body> </html>
  • 36. 從 JSP 到 Servlet package 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 { // 略... } }
  • 37. 從 JSP 到 Servlet
  • 38. 從 JSP 到 Servlet public 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); } // 略... }
  • 39. 從 JSP 到 Servlet • 在<%!與%>之間宣告的程式碼,都將轉譯 為Servlet中的類別成員或方法 • <%與%>之間所包括的內容,將被轉譯為 Servlet原始碼_jspService()方法中的內 容 • <%=與%>運算式元素中的運算式,會直接 轉譯為out物件print()輸出時的指定內 容
  • 40. 從 JSP 到 Servlet • 隱含物件,其實就是_jspService()中的 區域參考名稱… application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); 這麼說Servlet作的到的,JSP 都作的到 … 原則上是…因為都是轉成Servlet…只不過角色 職責不同…這是另一個故事了…
  • 42. 自訂標籤處理 聽說使用 JSTL 就不用吃義大利麵 … 那是新的 HTML 標籤嗎?。。XD 別傻了...我只處理 Java 的東西…每個標籤 後面都還是 Java 物件在處理事情。。XD
  • 44. 自訂標籤處理 • 當JSP網頁中包括Simple Tag自訂標籤 ,在轉 譯之後... 1. 建立自訂標籤處理器實例。 2. 呼叫標籤處理器的setJspContext()方法設定 PageContext實例。 3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理 器的setParent()方法,並傳入外層標籤處理器的 實例。 4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的 setTest()方法來設定)。 5. 呼叫標籤處理器的setJspBody()方法設定 JspFragment實例。 6. 呼叫標籤處理器的doTag()方法。 7. 銷毀標籤處理器實例。
  • 45. <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實作標籤時,你的標籤處 理器不能太肥大…
  • 46.
  • 47. 自訂標籤處理 • 當JSP中遇到TagSupport自訂標籤時 1. 嘗試從標籤池(Tag Pool)找到可用的標籤物件,如果找到就 直接使用,如果沒找到就建立新的標籤物件。 2. 呼叫標籤處理器的setPageContext()方法設定 PageContext實例。 3. 如果是巢狀標籤中的內層標籤,則還會呼叫標籤處理器的 setParent()方法,並傳入外層標籤處理器的實例。 4. 設定標籤處理器屬性(例如這邊是呼叫IfTag的setTest()方 法來設定)。 5. 呼叫標籤處理器的doStartTag()方法,並依不同的傳回值決 定是否執行本體或呼叫doAfterBody()、doEndTag()方法。 6. 將標籤處理器實例置入標籤池中以便再度使用。
  • 48. 自訂標籤處理 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完成…
  • 49. 自訂標籤處理 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; } } … }
  • 50. 自訂標籤處理 我記得還有個 Tag File 的東西… 是啦!不過…Tag File 會被我轉成 Simple Tag 的實作…XD package 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 {
  • 52. Thanks 林信良 http://openhome.cc caterpillar@openhome.cc 52