[Java] Servlet工做原理之一:體系結構及其容器

1、Servlet體系結構html

在 servlet-api.jar (2.5) 中有兩個包:javax.servlet 和 javax.servlet.http前端

           

1 Servlet、GenericServlet及HttpServletjava

Servlet 是一個接口,其方法以下:web

  • public void init(ServletConfig config);
  • public void service(ServletRequest req, ServletResponse res);
  • public void destroy();
  • public String getServletInfo(); // 返回servlet的信息,如做者、版本和版權
  • public ServletConfig getServletConfig(); // 獲取servlet配置屬性對象

GenericServlet 實現了 Servlet接口,是一個通用的、不特定於任何協議的 Servletspring

  • public void init()
  • public void init(ServletConfig config)
  • public abstract void service(ServletRequest req, ServletResponse res)
  • public void destroy()
  • public void log(String msg) // 將消息寫入日誌,利用ServletContext的方法寫入
  • public void log(String message, Throwable t)
  • public String getInitParameter(String name) // 獲取初始化參數,利用ServletConfig的方法獲取
  • public Enumeration getInitParameterNames()
  • public String getServletName()
  • public String getServletInfo() // 返回servlet的信息,如做者、版本和版權
  • public ServletConfig getServletConfig()
  • public ServletContext getServletContext()

HttpServlet 繼承於 GenericServlet,針對於 HTTP 協議的類apache

  • public void service(ServletRequest req, ServletResponse res)
  • protected void service(HttpServletRequest req, HttpServletResponse resp)
  • protected void doGet(...)、doPost(...)、doHead(...)、doPut(...)、doDelete(...)、doOptions(...)、doTrace(...)
  • protected long getLastModified(HttpServletRequest req) // 最後修改時間,應該重寫實現這個方法

2 ServletConfig、ServletContextapi

ServletConfig 對象儲存了 Servlet 的一些配置屬性,在 Servlet 執行 init() 方法時傳入,其方法以下:數組

  • public String getServletName(); // 獲取Servlet名稱
  • public String getInitParameter(String name); // 獲取初始化參數值
  • public Enumeration getInitParameterNames(); // 獲取全部參數名稱
  • public ServletContext getServletContext();

另外在 ServletConfig 中還有一個 ServletContext,它定義了有關 Servlet 容器的方法,其方法以下:瀏覽器

  • public String getContextPath(); // 返回web項目的路徑 
  • public String getRealPath(String path);
  • public URL getResource(String path); // 返回webapp下的文件路徑對應的URL
  • public Set getResourcePaths(String path); // 返回path路徑下的目錄或文件
  • public InputStream getResourceAsStream(String path); // 返回path路徑的資源
  • public ServletContext getContext(String uripath);
  • public RequestDispatcher getRequestDispatcher(String path);
  • public RequestDispatcher getNamedDispatcher(String name);
  • public String getMimeType(String file); // 返回指定文件的類型,如 text/html、image/gif
  • public String getServerInfo(); // 返回Servlet容器的名稱和版本 
  • public String getServletContextName(); // 返回這個web應用程序名稱
  • public String getInitParameter(String name);
  • public Enumeration getInitParameterNames();
  • public Enumeration getAttributeNames(); // 返回Servlet容器的全部屬性
  • public Object getAttribute(String name); // 返回Servlet容器的指定屬性 
  • public void setAttribute(String name, Object object);
  • public void removeAttribute(String name);
  • public void log(String msg); // 將消息寫入到日誌文件中 
  • public void log(String message, Throwable throwable);
  • public int getMajorVersion(); // 返回這個容器支持的Servlet主版本,如2.5返回2 
  • public int getMinorVersion(); // 返回這個容器支持的Servlet小版本,如2.5返回5 

3 ServletRequest、ServletResponse緩存

當請求達到時,容器將 ServletRequest 和 ServletResponse 傳遞給 Servlet。

ServletRequest 接口的方法以下:

  • public Enumeration getAttributeNames();
  • public Object getAttribute(String name);
  • public void setAttribute(String name, Object o);
  • public void removeAttribute(String name);
  • public void setCharacterEncoding(String env); // 設置請求體的編碼類型,讀取參數前使用
  • public String getCharacterEncoding(); // 獲取請求體的編碼類型
  • public String getContentType(); // 請求體的類型
  • public int getContentLength(); // 請求體的長度,長度未知則返回-1
  • public ServletInputStream getInputStream(); // 請求體的字節流
  • public BufferedReader getReader(); // 請求體的字符流
  • public String getParameter(String name); // 名爲name的參數值
  • public String[] getParameterValues(String name); // 名爲name的參數值,是一個數組
  • public Enumeration getParameterNames(); // 全部參數名稱
  • public Map getParameterMap(); // 全部參數的名稱和值
  • public String getProtocol(); // 請求的協議版本,如 HTTP/1.1
  • public String getScheme(); // 請求的協議方式,如 http https ftp
  • public String getServerName(); // 服務端的主機、服務器名或服務器IP地址
  • public int getServerPort(); // 服務端的端口號
  • public String getRemoteHost(); // 客戶端或最終代理的主機名稱
  • public String getRemoteAddr(); // 客戶端或最終代理的IP地址
  • public int getRemotePort(); // 客戶端或最終代理的端口號
  • public String getLocalName();
  • public String getLocalAddr();
  • public int getLocalPort();
  • public Locale getLocale(); // 返回請求頭Accept-Language設置的語言環境
  • public Enumeration getLocales();
  • public boolean isSecure(); // 是否使用HTTPS等安全通道進行的請求
  • public RequestDispatcher getRequestDispatcher(String path);

ServletResponse 接口的方法以下:

  • public String getCharacterEncoding(); // 獲取響應體的編碼類型
  • public void setCharacterEncoding(String charset); // 設置響應體編碼類型
  • public String getContentType(); // 獲取響應體的類型
  • public void setContentType(String type); // 設置響應體的類型
  • public void setContentLength(int len); // 設置響應體長度
  • public ServletOutputStream getOutputStream(); // 獲取響應體的字節流
  • public PrintWriter getWriter(); // 獲取響應體的字符流
  • public int getBufferSize(); // 返回實際緩衝大小,不使用緩衝則爲0
  • public void setBufferSize(int size); // 設置響應體緩衝大小
  • public void flushBuffer(); // 將緩衝區內容寫入到客戶端
  • public void resetBuffer(); // 清除緩衝區數據,若是緩衝已經被寫入客戶端,則拋異常
  • public void reset(); // 清除緩衝區的數據、狀態碼及響應頭,若是已經寫入客戶端,則拋異常
  • public boolean isCommitted(); // 響應是否已提交
  • public void setLocale(Locale loc);
  • public Locale getLocale();

HttpServletRequest 和 HttpServletResponse 接口分別繼承自 ServletRequest 和 ServletResponse,在其基礎上

HttpServletRequest 接口增長的方法以下:

  • public String getAuthType();
  • public Cookie[] getCookies();
  • public long getDateHeader(String name);
  • public int getIntHeader(String name);
  • public String getHeader(String name);
  • public Enumeration getHeaders(String name);
  • public Enumeration getHeaderNames();
  • public String getMethod();
  • public String getPathInfo();
  • public String getPathTranslated();
  • public String getContextPath();
  • public String getQueryString();
  • public String getRemoteUser();
  • public boolean isUserInRole(String role);
  • public java.security.Principal getUserPrincipal();
  • public String getRequestedSessionId();
  • public String getRequestURI();
  • public StringBuffer getRequestURL();
  • public String getServletPath();
  • public HttpSession getSession(boolean create);
  • public HttpSession getSession();
  • public boolean isRequestedSessionIdValid();
  • public boolean isRequestedSessionIdFromCookie();
  • public boolean isRequestedSessionIdFromURL();

HttpServletResponse 接口增長的方法以下:

  • public void addCookie(Cookie cookie);
  • public boolean containsHeader(String name);
  • public String encodeURL(String url);
  • public String encodeRedirectURL(String url);
  • public void sendError(int sc, String msg) throws IOException;
  • public void sendError(int sc) throws IOException;
  • public void sendRedirect(String location) throws IOException;
  • public void setDateHeader(String name, long date);
  • public void addDateHeader(String name, long date);
  • public void setHeader(String name, String value);
  • public void addHeader(String name, String value);
  • public void setIntHeader(String name, int value);
  • public void addIntHeader(String name, int value);
  • public void setStatus(int sc);

上述的四個接口分別有一個包裝類的實現,利用了裝飾者模式。

 

4 Filter、FilterConfig、FilterChain

Filter 即過濾器,它是 AOP 思想的一種實現(利用回調函數實現的),經過它咱們能夠實現權限訪問控制、過濾敏感詞彙、日誌記錄等等。爲何要使用 Filter 呢?或者說爲何要使用 AOP 的方式去作這個呢?若是咱們不使用 Filter 而直接在 Servlet 的 doGet()、doPost() 方法中實現上述功能也是能夠的,可是這樣致使了代碼冗餘,因此咱們須要把這些公共的代碼抽象出來進行封裝。像 OOP 的封裝方式針對的是對具備上下關係的對象,而像訪問控制、日誌等功能並不適合這樣的封裝,它更像是一種左右關係,因此咱們要用 AOP 的方式進行封裝。

Filter 能夠實如今 Servlet 的 service() 調用的先後執行一段代碼,從而實現了公共代碼的複用。使用 Filter 與 Servlet 類似,首先要本身編寫一個類實現 Filter 接口,而後在 web.xml 中配置好直接該 Filter 對應的 URL。Filter 中有一個 doFilter() 方法,其使用方式大體以下

public class FilterTest implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init");
        // 獲取過濾器的名字
        String filterName = filterConfig.getFilterName();
           // 獲取其初始化參數,在 web.xml 中指定的
           String param1 = filterConfig.getInitParameter("name");
           String param2 = filterConfig.getInitParameter("like");
        // 返回過濾器的全部初始化參數的名字的枚舉集合。
        Enumeration<String> paramNames = filterConfig.getInitParameterNames();
        System.out.println(filterName);
        System.out.println(param1);
        System.out.println(param2);
        while (paramNames.hasMoreElements()) {
                String paramName = (String) paramNames.nextElement();
                System.out.println(paramName);
        }
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,    
            FilterChain chain) throws ServletException, IOException {
        // 執行前的操做
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        System.out.println("before");
        // 執行service()方法或下一個過濾器方法
        chain.doFilter(request, response); //讓目標資源執行,放行
        // 執行後的操做
        System.out.println("after");
    }
    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

編寫完 Filter 實現類後還要在 web.xml 文件中對其註冊和映射

<!-- filter註冊 -->
<filter>
    <filter-name>FilterTest</filter-name>
    <filter-class>com.filter.FilterTest</filter-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>t</param-value>
    </init-param>
    <init-param>
        <param-name>like</param-name>
        <param-value>java</param-value>
    </init-param>
</filter>
<!-- filter映射 -->
<filter-mapping>
    <filter-name>FilterTest</filter-name>
    <url-pattern>*.do</url-pattern>
    <!-- 指定過濾器所攔截的 Servlet 名稱
    <servlet-name></servlet-name> -->
    <!-- 指定過濾器所攔截的資源被 Servlet 容器調用的方式,
         REQUEST:用戶直接訪問時調用,即不包括經過RequestDispatcher訪問的狀況
         INCLUDE:經過RequestDispatcher的include()方法訪問時調用
         FORWARD:經過RequestDispatcher的forward()方法訪問時調用
         ERROR:若是目標資源是經過聲明式異常處理機制調用時,那麼該過濾器將被調用
         默認REQUEST,而且能夠設置多個<dispatcher>
    <dispatcher></dispatcher> -->
</filter-mapping>

咱們能夠編寫多個 Filter,組成了一個 Filter 鏈。執行順序與它們在 web.xml 文件中配置順序有關,先配置則先執行。在上述代碼中,咱們調用了 FilterChain 對象的 doFilter() 方法,此時會先檢查 FilterChanin 對象中是否還有下一個 Filter,若是有則繼續調用,若是沒有則調用 Servlet 的 service() 方法。

4.1 Filter

Filter 的建立和銷燬由其容器負責,容器啓動的時候建立 Filter 實例對象,並調用 init() 方法完成初始化,Filter 只會實例化一次。

  • public void init(FilterConfig filterConfig); // 初始化並傳入Filter的配置對象
  • public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain); // 執行攔截器的內容
  • public void destroy(); // Filter銷燬時調用,當這個方法被調用後,容器還會再調用一次 doFilter() 方法

4.2 FilterConfig

  • public String getFilterName();
  • public ServletContext getServletContext();
  • public String getInitParameter(String name);
  • public Enumeration getInitParameterNames();

4.3 FilterChain

Filter 類的核心就是傳遞 FilterChain 對象,在 Tomcat 中 FilterChain 的實現類是 ApplicationFilterChain,它在 filters 數組中保存了到最終 Servlet 對象的全部 Filter 對象,當執行完全部 Filter 對象後就會執行 Servlet。

  • public void doFilter(ServletRequest request, ServletResponse response);

 

5 RequestDispatcher

  • public void forward(ServletRequest request, ServletResponse response)
  • public void include(ServletRequest request, ServletResponse response)

 

6 Listener

Listener 是基於觀察者模式設計的,可以方便的從另外一個縱向維度控制程序和數據。在 Servlet 中有兩類共6中觀察者接口,EventListeners 類型的 ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttrbuteListener,還有 LifecycleListeners 類型的 ServletContextListener、HttpSessionListener,如圖所示

這些標籤的實現類能夠配置在 web.xml 的 <listener> 標籤中,也能夠在程序中動態的添加。如 Spring 的 org.springframework.web.context.ContextLoaderLister 就實現了一個 ServletContextListener,當容器加載時啓動 Spring,以下所示

<!-- spring啓動監聽器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring配置文件,默認查找 WEB-INF 下的 applicationContext.xml 文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
</context-param>

下面咱們看一下各個 Listener 的具體方法:

ServletContextListener

  • public void contextInitialized(ServletContextEvent sce); //在 Context 容器初始時,Filter 和 Servlet 的 init() 前調用
  • public void contextDestroyed(ServletContextEvent sce); //在 Context 容器銷燬時,Filter 和 Servlet 的 destroy() 後調用

ServletRequestListener

  • public void requestInitialized(ServletRequestEvent sre); // HttpServeltRequest 傳遞到 Servlet 前調用
  • public void requestDestroyed(ServletRequestEvent sre); 

HttpSessionListener

  • public void sessionCreated(HttpSessionEvent se);
  • public void sessionDestroyed(HttpSessionEvent se);

HttpSessionBindingListener

  • public void valueBound(HttpSessionBindingEvent event); // 對象被放入 session 時調用
  • public void valueUnbound(HttpSessionBindingEvent event); // 對象被移出 session 時調用

ServletContextAttributeListener

  • public void attributeAdded(ServletContextAttributeEvent scab); //調用 servletContext 的 setAttribute() 時觸發
  • public void attributeRemoved(ServletContextAttributeEvent scab); //調用 servletContext 的 removeAttribute() 時觸發
  • public void attributeReplaced(ServletContextAttributeEvent scab); //調用 servletContext 的 setAttribute() 替換舊值時觸發

ServletRequestAttributeListener

  • public void attributeAdded(ServletRequestAttributeEvent srae);
  • public void attributeRemoved(ServletRequestAttributeEvent srae);
  • public void attributeReplaced(ServletRequestAttributeEvent srae);

HttpSessionAttributeListener

  • public void attributeAdded(HttpSessionBindingEvent se);
  • public void attributeRemoved(HttpSessionBindingEvent se);
  • public void attributeReplaced(HttpSessionBindingEvent se);

HttpSessionActivationListener

  • public void sessionWillPassivate(HttpSessionEvent se); // 通知 session 被鈍化
  • public void sessionDidActivate(HttpSessionEvent se); // 通知 session 被激活

 

7 ServletInputStream、ServletOutputStream

 

8 ServletException

 

2、Tomcat 組件

Servlet 不可以獨立運行,須要在它的容器中運行,容器管理着它建立到銷燬的整個過程。在看 Servlet 的生命週期前,咱們先看下 Servlet 的咱們最熟悉的一個容器——Tomcat。

Tomcat 有兩個重要組件:鏈接器(Connector)和容器(Engine容器及其子容器),咱們結合 server.xml 配置文件來看一下這兩個組件。

1 鏈接器(Connector)

首先向 Tomcat 發送的請求能夠分爲兩類:

  • Tomcat 做爲應用服務器:請求來自前端的 Web 服務器,如 Nginx、Apache、IIS 等。
  • Tomcat 做爲獨立服務器:請求來自瀏覽器。

這些不一樣的請求須要不一樣的鏈接器來接收,在 Service 中有一個引擎和多個鏈接器,以適應不一樣狀況。常見的鏈接器有四種:HTTP鏈接器、SSL鏈接器、AJP鏈接器、proxy鏈接器。在定義鏈接器時能夠配置的屬性有不少,鏈接器公用屬性以下:

  • className 指定實現 Connector 接口的類
  • enableLookups 是否經過request.getRemoteHost()獲取客戶端的主機名,默認true
  • redirectPort 若是鏈接器的協議是HTTP,當收到HTTPS請求時,轉發到此端口

HttpConnector 的屬性:

  • className 指定實現 Connector 接口的類
  • port 監聽端口,默認8080
  • address 指定監聽地址,默認爲全部地址
  • bufferSize 設置由端口建立的輸入流緩存大小,默認2048byte
  • protocol 鏈接器使用的協議,默認HTTP/1.1
  • maxThreads 支持的最大併發鏈接數,默認200
  • connectionTimeout 等待客戶端發送請求的超時時間,默認60000,即1分鐘
  • acceptCount 設置等待隊列的最大長度,默認爲10。當tomcat全部處理線程均繁忙時,新連接被放置於等待隊列中

JkConnector 的屬性:

  • className 指定實現 Connector 接口的類
  • port 設定AJP端口號
  • protocol 必須爲 AJP/1.3

2 容器(Engine容器及其子容器)

在 Tomcat 中有 Engine、Host、Context 及 Wrapper 四種容器,它們的包含關係以下圖所示

上述的包含並非繼承關係,而是當子容器建立好後會放入到父容器中。Servlet 被包裝成 Wrapper,而後真正管理 Servlet 的是 Context 容器,一個 Context 對應一個 Web 應用。

  • Wrapper 封裝了具體訪問的資源,即 Servlet;
  • Context 封裝了各個 Wrapper 資源的集合;
  • Host 封裝了 Context 資源的集合;
  • Engine 能夠當作是對 Host 的邏輯封裝。

咱們再來看一下它們的繼承關係,這些容器的接口都繼承自 Container 接口,爲何要按層次分別封裝一個對象呢?爲了方便統一管理,在不一樣層次的配置其做用域是不同的。

2.1 Engine

Engine 下面擁有多個 Host,即虛擬主機,它的責任就是將用戶的請求分配給一個虛擬主機處理。爲何要使用虛擬主機呢?當咱們有兩個應用時,以下圖的 Love 應用和 SDJTU 應用。咱們想訪問「倪培.我愛你」域名時直接達到 Love 應用,訪問「www.sdjtu.net.cn」域名時直接到達 SDJTU 應用,可是若是不設置虛擬主機是沒法在一個 Tomcat中作到的。那麼,咱們能夠設置兩個虛擬主機,並指定請求到達這個虛擬主機後要去訪問的目錄。

在 Engine 標籤中有幾個屬性能夠填寫

  • name 定義 Engine 的名字
  • className 指定實現 Engine 接口的類,默認是 StandardEngine
  • defaultHost 指定處理請求的默認主機

在 Engine 標籤裏還能夠包含如下幾個元素

  • Logger
  • Realm
  • Valve
  • Host

2.2 Host

Host 表明一個虛擬主機,在它下面有多個 Context,一個 Context 表明一個 Web 應用。

在 Host 標籤中的幾個屬性

  • name 定義 Host 的名字
  • className 指定實現 Host 接口的類,默認是 StandardHost
  • appBase 指定虛擬主機的目錄,默認是 webapps
  • unpackWARs 是否先展開war文件再運行。若是爲 false 將直接運行 war 文件
  • autoDeploy 表示是否支持熱部署
  • alias 用來指定主機別名
  • deployOnStartup 是否在啓動時自動發佈目錄下的全部Web應用

在 Host 標籤中還能夠包含如下幾個元素

  • Logger 
  • Realm
  • Valve
  • Host

2.3 Context

Context 表明運行在虛擬主機上的單個 Web 應用。

在 Context 標籤中的幾個屬性

  • className 指定現實 Context 接口的類,默認是 StandardContext 類
  • path 配置Web應用對應的URL,即跟在域名後面的內容
  • docBase 指定要執行的Web應用
  • reloadable 當項目下的 class 文件被更新時,是否從新加載Web應用
  • cookies 指定是否經過 Cookies 來支持 Session,默認爲 true
  • useNaming 指定是否支持 JNDI,默認值爲 ture

在 Context 標籤中的元素

  • Logger
  • Realm
  • Resource
  • ResourceParams

 

2 Tomcat 啓動過程

Tomcat 從 7.0 開始增長了一個啓動類 org.apache.catalina.startup.Tomcat。經過這個類的實例調用 start() 方法就能夠啓動 Tomcat,還能夠經過這個對象增長和修改 Tomcat 的配置參數,來動態的添加 Context、Servlet 等。

Tomcat 的啓動是基於觀察者模式設計的,全部的容器都繼承了 Lifecycle 接口,由它來管理容器的生命週期,全部容器的修改和狀態改變都會由它去通知已經註冊的觀察者(Listener)。

當 Context 容器初始化狀態爲 init 時,添加到 Context 容器的 Listener 將會被調用。ContextConfig 繼承了 LifecycleListener 接口,它是在調用了 Tomcat.addWebapp 時被加入到 StandardContext 容器的,這個類將會負責整個 Web 應用的配置解析工做。ContextConfig 的 init 方法將會主要完成如下工做:

  1. 建立 ContextDigester 對象來解析 XML 配置文件
  2. 讀取默認的 context.xml 配置文件,若是存在則解析它
  3. 讀取默認的 Host 配置文件,若是存在則解析它
  4. 讀取默認的 Context 自身的配置文件,若是存在則解析它
  5. 設置 Context 的 DocBase

當 ContextConfig 的 init 方法完成後,Context 容器會執行 startInternal 方法,主要包括如下工做

  1. 建立讀取資源文件的對象
  2. 建立 ClassLoader 對象
  3. 設置應用的工做目錄
  4. 啓動相關的輔助類,如 logger、realm、resources 等
  5. 修改啓動狀態,通知感興趣的觀察者
  6. 子容器的初始化
  7. 獲取 servletContext 並設置必要參數
  8. 初始化「load on startup」的 Servlet

Web 應用的初始化是在 ContextConfig 的 configureStart 方法中實現的,應用初始化主要是解析 web.xml 文件。web.xml 文件中的配置會被解析成 WebXml 對象,而後這些配置會放入 Context 中,而且 Servlet 配置會被包裝成 StandardWrapper 並做爲子容器添加到 Context 中。

 

3、Servlet 生命週期

前面咱們知道 Servlet 由 Tomcat 解析,並被包裝成 Wrapper 添加在 Context 容器中,下面就要進行 Servlet 的實例化。

1 建立實例

建立 Servlet 實例的方法是從 StandardWrapper 的 loadServlet() 方法開始的。loadServlet() 方法獲取了 servletClass,而後將它交給了 InstanceManager 去建立一個基於 ServletClass.class 的對象。

Servlet 並非單例的,但通常只會有一個實例,即一個<servlet>標籤對應一個實例。另外若是 Servlet 沒有配置<servlet-mapping>標籤,則沒法經過請求時建立,只能配置 load-on-startup 使其在容器啓動時便建立。 

2 初始化

初始化 Servlet 是在 StandardWrapper 對象的 initServlet() 方法中,這個方法會去調用 Servlet 的 init() 方法,同時把 StandardWrapperFacade 對象做爲 ServletConfig 傳遞進去。

3 處理請求

客戶端發出 Http 請求,Tomcat 接收到請求後將信息封裝進了 HttpRequest 對象,接着建立一個 HttpResponse 對象,而後調用 HttpServlet 對象的 service() 方法,把 HttpRequest 對象與 HttpRespnse 對象傳入進去。當執行完 service() 方法後,Tomcat 把響應傳遞給客戶端。

4 銷燬

相關文章
相關標籤/搜索