1. 程式人生 > >Java Web基礎 --- Servlet 綜述(理論篇)

Java Web基礎 --- Servlet 綜述(理論篇)

摘要:

  Web 技術成為當今主流的網際網路 Web 應用技術之一,而 Servlet 是 Java Web 技術的核心基礎。本文首先從請求/響應架構應用的大背景談起 Servlet 的由來,明確 Servlet 的產生動機,並揭示了 Servlet 的本質以及其在標準MVC模式中所扮演的角色。緊接著,給出了 Servlet族的繼承結構,並對族內的介面和抽象類作了進一步的介紹,並給出開發一個Servlet程式的常用做法。在此基礎上,我們圖文並茂地介紹了 Servlet 的生命週期與執行流程,清晰展現 Servlet 的原理。特別地,由於Servlet容器預設採用單例項多執行緒的方式來處理請求,我們進一步介紹了Servlet 與併發的聯絡,並就Servlet容器如何同時來處理多個請求和如何開發執行緒安全的Servlet兩個問題進行討論和總結。最後,結合Java Web 應用的結構演變歷程,給出了MVC架構的基本組成要素、內在聯絡和作用分工,並給出了 Servlet 在其中所扮演的角色。

  本篇主要介紹 Servlet 理論方面的知識,更多關注於以下幾個問題:

  • 為什麼會有 Servlet;
  • Servlet 是什麼;
  • Servlet 如何實現預期效果;
  • Servlet 的作用原理;
  • Servlet 與 併發;
  • Servlet 與 Java Web 應用的結構演變歷程;
  • Servlet 與 MVC 的聯絡。

  更多關於Servlet使用、實踐方面的介紹以及Servlet新特性(Servlet 非同步處理、Servlet 非阻塞IO 以及 Servlet 檔案上傳等)的總結見本文的姊妹篇《Servlet 綜述(實踐篇)》
  
  更多關於Java併發相關知識請移步我的專欄

《Java併發程式設計學習筆記》。該專欄全面記錄了Java併發程式設計的相關知識,並結合作業系統、Java記憶體模型和相關原始碼對併發程式設計的原理、技術、設計、底層實現進行深入分析和總結,並持續跟進併發相關技術。

一. Servlet 概述

1、從請求/響應架構談 Servlet 的由來

  我們之前在博文《Java Web基礎 — Jsp 綜述(下)》中對 請求/響應架構 有過比較系統的敘述,現在我們作進一步敘述。

  我們日常所接觸到的應用有很大一部分都是基於 請求/響應架構 的,如下圖所示。在這種架構中,一般由兩個角色組成,即:ServerUser Agent

。特別地,根據 User Agent 的不同,我們可以將應用分為 B/S模式(User Agent 為瀏覽器時)C/S模式。但無論哪種模式,Server 與 User Agent 的互動使用的都是同一個請求和應答的標準,即 HTTP 協議

  一般地,以瀏覽器為例,User Agent 的作用就是根據使用者的請求URL生成相應的 HTTP請求報文傳送給伺服器,並對伺服器的響應進行解析(或渲染),使使用者看到一個豐富多彩的頁面。但是,如果我們需要在網頁上完成一些業務邏輯(比如,登陸驗證),或者需要從伺服器的資料庫中取一些資料作為網頁的顯示內容,那麼除了負責顯示的HTML標記之外,必須還要有完成這些業務功能的程式碼存在,這種網頁我們稱之為 動態網頁

  對於靜態網頁而言,伺服器上存在的是一個個純HTML檔案。當客戶端瀏覽器發出HTTP請求時,伺服器可以根據請求的URL找到對應的HTML檔案,並將HTML程式碼返回給客戶端瀏覽器。但是對於動態網頁,伺服器上除了找到需要顯示的HTML標記外,還必須執行所需要的業務邏輯,然後將業務邏輯運算後的結果和需要顯示的HTML標記一起生成新的HTML程式碼。最後,將新的帶有業務邏輯運算結果的HTML程式碼返回給客戶端。為了實現動態網頁的目標,Servlet技術(利用輸出流動態生成 HTML 頁面)應運而生,它能夠以一種可移植的方法來提供動態的、面向使用者的內容。

            請求響應.png-211.4kB

2、Servlet 的本質與角色

  Web 技術成為當今主流的網際網路 Web 應用技術之一,而 Servlet 是 Java Web 技術的核心基礎。包括我們在前面的博文中談到的JSP,也只是為了彌補使用 Servlet 作為表現層的不足而提出的。JSP規範通過實現普通靜態HTML和動態部分的混合編碼,使得邏輯內容與外觀相分離,大大簡化了表示層的實現。但是,JSP並沒有增加任何本質上不能用Servlet實現的功能,只是在JSP中編寫靜態HTML更加方便。事實上,JSP的本質仍然是Servlet,並且站在表現層的角度上來看,JSP 是 Servlet 的一種就簡化。

  Servlet 是 J2EE 標準的一部分,是一種執行在Web伺服器端的小型Java程式,更具體的說,Servlet 是按照Servlet規範編寫的一個Java類,用於互動式地瀏覽和修改資料,生成動態Web內容。要注意的是,由於 Servlet 是伺服器端小程式,所以 Servlet 必須部署在 Servlet 容器中才能使用,例如 Tomcat,Jetty 等。

  在標準的MVC模式中,Servlet 僅作為控制器使用,而控制器角色的作用是:負責接收客戶端的請求,它既不直接對客戶端輸出響應,也不處理使用者請求,只是呼叫業務邏輯元件(JavaBean)來處理使用者請求。一旦業務邏輯元件處理結束後,控制器會根據處理結果,呼叫不同的表現層頁面向瀏覽器呈現處理結果。

二. Servlet API

  關於 Servlet 的介面主要在以下兩個包中,Servlet 繼承結構如下圖所示:

  • javax.servlet.* :存放與HTTP 協議無關的一般性Servlet 類;

  • javax.servlet.http.* :除了繼承 javax.servlet.* 之外,還增加了與HTTP協議有關的功能。

      特別需要說明的是,所有的 Servlet 都必須實現 javax.servlet.Servlet 介面。若我們開發的 Servlet程式與HTTP協議無關,那麼可以直接繼承 javax.servlet.GenericServlet抽象類 ;否則,若我們開發的Servlet程式和HTTP協議有關,那麼可以直接繼承 javax.servlet.http.HttpServlet 抽象類

                Servlet-API.png-14.4kB              

  下面我們分別看一下 Servlet介面、ServletConfig介面、GenericServlet抽象類 和 HttpServlet抽象類給我們提供的介面:

(1) Interface Servlet

             InterfaceServlet.png-22.9kB

  Servlet 介面定義了所有Servlet都必須實現的方法。其中,destroy()方法、init()方法 和 service()方法 由Servlet容器來呼叫。特別地,service()方法用於處理並響應請求。

(2) Interface ServletConfig

  A servlet configuration object used by a servlet container to pass information to a servlet during initialization.

             InterfaceServletConfig.png-14kB

(3) Abstract Class GenericServlet

  GenericServlet implements the Servlet and ServletConfig interfaces. GenericServlet may be directly extended by a servlet, although it’s more common to extend a protocol-specific subclass such as HttpServlet.

  GenericServlet makes writing servlets easier. It provides simple versions of the lifecycle methods init and destroy and of the methods in the ServletConfig interface. GenericServlet also implements the log method, declared in the ServletContext interface.

  To write a generic servlet, you need only override the abstract service method.

             ServletGeneric.png-38.2kB

(4) Abstract Class HttpServlet

             HttpServlet.png-44.5kB

  通過繼承 HttpServlet 可以很方便的幫助我們建立一個 HTTP Servlet。我們可以看到,HttpServlet 為各種Http請求都提供了相應的處理方式。但是,我們從Servlet介面中我們瞭解到,service()方法用於生成對客戶端的響應,那麼 service()方法與圖中所示的七種Http請求處理方法有什麼聯絡呢?下面請看 HttpServlet 對service()方法的實現:

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
       }
    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);   
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
    } else {
        // Error
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

  我們知道,service()方法用於生成對客戶端的響應。但對於 HTTPServlet 而言,service()方法會按照具體的請求型別將請求進一步分發到對應的處理方法進行處理。由於在大部分時候,Servlet 對各種型別的請求的處理方式都是一樣的,因此我們只需重寫service()方法即可響應客戶端的所有請求。或者,另一種處理方式是,由於客戶端的請求通常只有 GET 和 POST 兩種,因此我們只需重寫doGet() 和 doPost()兩個方法即可。

  實際上,普通Servlet類裡的service()方法的作用,完全等同於JSP轉譯所生成Servlet類裡的_jspService()方法。

三. Servlet的生命週期與執行流程

1、Servlet的生命週期

  當 Servlet 在容器中執行時,其例項的建立及銷燬等都不是由程式設計師所決定的,而是由Web容器進行控制的。一個Servlet物件的建立有兩個時機:使用者請求之時或應用啟動之時。

 (1) 客戶端第一次請求某個Servlet時,容器建立該Servlet例項,這也是大部分Servlet建立例項的時機;
 (2) Web應用啟動時立即建立Servlet例項,即 load-on-startup Servlet。 

  但每個Servlet都遵循一個生命週期,即:Servlet 例項化–>Servlet 初始化—>服務—>銷燬。具體流程如下圖所示:

  • 建立Servlet例項;

  • Web容器呼叫Servlet的init()方法,對Servlet進行初始化。特別地,在Servlet的生命週期中,僅執行一次init()方法;

  • Servlet 被初始化後,將一直存在於Web容器中,用於響應客戶端請求。預設的請求處理、響應方式是呼叫與HTTP請求的方法相應的do方法;

  • Web容器決定銷燬Servlet時,先呼叫Servlet的 destroy() 的方法回收資源,通常在關閉Web應用之時銷燬 Servlet。和 init() 方法類似,destroy()方法在Servlet的生命週期中也僅執行一次。當Servlet物件退出生命週期時,負責釋放佔用的資源。一個Servlet在執行service()方法時可能會產生其他的執行緒,因此需要確保在呼叫destroy()方法時,這些執行緒已經終止或完成。

              Servlet生命週期.png-235.9kB

2、Servlet的執行流程

 Servlet的執行流程如下圖所示:

  (1) User Agent 向 Servlet容器(Tomcat)發出Http請求;
  (2) Servle容器接收 User Agent 發來的請求;
  (3) Servle容器根據web.xml檔案中Servlet相關配置資訊,將請求轉發到相應的Servlet;
  (4) Servlet容器建立一個 HttpServlet物件,用於處理請求;
  (5) Servlet容器建立一個 HttpServletRequest物件,將請求資訊封裝到這個物件中;
  (6) Servlet容器建立一個HttpServletResponse物件;
  (7) Servlet容器呼叫HttpServlet物件的service方法,並把HttpServltRequest物件與HttpServltResponse物件作為引數傳給 HttpServlet 物件;
  (8) HttpServlet呼叫HttpServletRequest物件的有關方法,獲取Http請求資訊;
  (9) HttpServlet呼叫JavaBean物件(業務邏輯元件)處理Http請求;
  (10) HttpServlet呼叫HttpServletResponse物件的有關方法,生成響應資料;
  (11) Servlet容器將HttpServlet的響應結果傳給 User Agent。

             servlet執行流程.png-14.8kB

3、load-on-servlet

  load-on-servlet 指的是應用啟動時就建立的Servlet,這些Servlet通常是用於後臺服務的Servlet或者需要攔截很多請求的Servlet。也就是說,這些Servlet常常作為應用的基礎Servlet使用,提供重要的後臺服務。例如:

@WebServlet(loadOnStartup=1)
public class TimerServlet extends HttpServlet
{
    public void init(ServletConfig config)throws ServletException
    {
        super.init(config);
        Timer t = new Timer(1000,new ActionListener()       // 匿名內部類
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date());
            }
        });
        t.start();
    }
}

  我們看到,這個 load-on-servlet 沒有提供 service()方法,這表明它不能響應使用者請求,所以無需為它配置URL對映。由於它不能接收使用者請求,所以只能在應用啟動時例項化。

  特別需要注意的是,loadOnStartup屬性用於標記容器是否在啟動的時候載入這個servlet。當值為0或者大於0時,表示容器在應用啟動時就載入這個servlet;當是一個負數時或者沒有指定時,則指示容器在該servlet被選擇時才載入。其中,正數值越小,啟動該servlet的優先順序越高。

四. Servlet的配置

  為了讓Servlet能夠響應使用者請求,必須將Servlet配置到Web應用中。從 J2EE 6(即 Servlet 3.0)開始,配置Servlet有兩種方式,即 @WebServlet註解配置 和 傳統的 web.xml 配置,這部分內容在我的下一篇部落格《Servlet 綜述(實踐篇)》有詳細介紹,此不贅述。

五. Servlet 與 併發

1、Servlet容器如何同時來處理多個請求

  Servlet 採用多執行緒來處理多個請求同時訪問。更具體地,Servlet 依賴於一個執行緒池來服務請求,所謂執行緒池實際上是一系列的工作者執行緒集合,該集合包含的是一組等待執行任務的執行緒。此外,Servlet 使用一個排程執行緒來管理這些工作者執行緒。

  當Servlet容器收到一個Servlet請求時,排程執行緒會從執行緒池中選出一個工作者執行緒,並將請求傳遞給該工作者執行緒,然後由該執行緒來執行Servlet的service()方法。當這個執行緒正在執行的時候,如果容器收到另外一個請求,排程執行緒將同樣從執行緒池中選出另一個工作者執行緒來服務新的請求,特別需要注意的是,容器並不關心這個請求是否訪問的是同一個Servlet。當容器同時收到對同一個Servlet的多個請求時,那麼這個Servlet的service()方法將在多執行緒中併發執行。

  Servlet容器預設採用單例項多執行緒的方式來處理請求,這樣減少產生Servlet例項的開銷,提升了對請求的響應時間,對於Tomcat容器,我們可以在其server.xml中通過元素設定執行緒池中的執行緒數目。

2、如何開發執行緒安全的Servlet

  Servlet容器採用多執行緒來處理請求,提高效能的同時也造成了執行緒安全問題。要開發執行緒安全的 Servlet應該從這幾個方面進行:

(1). 變數的執行緒安全: 多執行緒並不共享區域性變數,所以我們要儘可能的在Servlet中使用區域性變數;

(2). 程式碼塊的執行緒安全: 可以使用Synchronized、Lock 和 原子操作(java.util.concurrent.atomic)來保證多執行緒對共享變數的協同訪問;但是要注意的是,要儘可能得縮小同步程式碼的範圍,儘量不要在service方法和響應方法上直接使用同步,這會嚴重影響效能;

(3). 屬性的執行緒安全: ServletContext,HttpSession,ServletRequest物件中屬性是執行緒安全的;

(4). 使用執行緒安全容器: 使用 java.util.concurrent 包下的執行緒安全容器代替 ArrayList、HashMap 等非執行緒安全容器;

  更多關於Java併發相關知識請移步我的專欄《 Java併發程式設計學習筆記》。該專欄全面記錄了Java併發程式設計的相關知識,並結合作業系統、Java記憶體模型和相關原始碼對併發程式設計的原理、技術、設計、底層實現進行深入分析和總結,並持續跟進併發相關技術。

六. Servlet 與 MVC

  Java Web 應用的結構經歷了 Model1 和 Model2 兩個時代。在 Model1 模式下,整個 Web 應用幾乎全部用JSP頁面組成,只用少量的JavaBean來處理資料庫連線、訪問等操作。從工程化角度來看,JSP 不但充當了表現層角色,還充當了控制器角色,將控制邏輯和表現邏輯混雜在一起,導致程式碼重用率極低,使得應用極難擴充套件和維護。

  Model2 已經是基於MVC架構的設計模式。在 Model2 中,Servlet 作為前端控制器,負責接收客戶端傳送的請求,在 Servlet 中只包含控制邏輯和簡單的前端處理;然後,Servlet 呼叫後端的JavaBean(業務邏輯元件)來處理業務邏輯;最後,根據處理結果轉發到相應的JSP頁面處理顯示邏輯。在 Model2 模式下,模型(Model)由 JavaBean 充當,檢視(View)由JSP頁面充當,而控制器則由 Servlet 充當。Model2 的流程示意圖如下:

              Model2.png-32.7kB

  更具體地,在 Model2(標準MVC)中,角色分工如下:

  • Model:由 JavaBean 充當,所有的業務邏輯、資料庫訪問都在Model中實現;

  • View:由 JSP 充當,負責收集使用者請求引數並將應用處理結果、狀態資料呈現給使用者;

  • Controller:由 Servlet 充當,作用類似於排程員,即所有使用者請求都發送給 Servlet,Servlet 呼叫 Model 來處理使用者請求,並根據處理結果呼叫 JSP 來呈現結果;或者Servlet直接呼叫JSP將應用處理結果展現給使用者。

相關推薦

Java Web基礎 --- Servlet 綜述(理論)

摘要:   Web 技術成為當今主流的網際網路 Web 應用技術之一,而 Servlet 是 Java Web 技術的核心基礎。本文首先從請求/響應架構應用的大背景談起 Servlet 的由來,明確 Servlet 的產生動機,並揭示了 Servlet 的本質

Java Web基礎 --- Jsp 綜述(下)

摘要:   JSP指令碼中包含九個內建物件,它們都是Servlet-API介面的例項,並且JSP規範對它們進行了預設初始化。本文首先通過一個JSP例項來認識JSP內建物件的實質,緊接著以基於請求/響應架構應用的執行機制為背景,引出JSP/Servlet的通訊方

Java web基礎學習之開發環境

work eclipse 軟件 ase java lips pda down ava Tomcat + Eclipse添加Java EE插件 因為之前進行Java SE學習已經配置了JDK,安裝了Eclipse for Java SE,所以選擇了在Eclipse上添加插件的

Java WebServlet):簡介

目錄 Servlet 簡介 servlet 優勢 Servlet 架構 Servlet 任務 Servlet 包 Servlet 基本結構 Servlet 生命週期 init() 方法 service() 方法 doGet() 方法 doPost

Java Web基礎知識之Servlet(3):Session管理

Session 管理是Web應用開發中的一個重要的內容,其實每天我們瀏覽網站,網站的後臺都是通過這門技術來記錄我們的瀏覽狀態,最典型的就是登入,每次你在網站上登入一次,當跳轉到該網站的任何其他頁面都不會再次要求你登入,這就是使用了Session管理技術。那麼問題來了我們為什

Java Web基礎知識之Servlet容器初始化(無web.xml)

在之前典型的Java Web程式中,部署描述符web.xml是必不可少的,在這裡我們需要配置各種元件,包括Servlet、Filter和Listener等,如果使用過SpringMVC的話,應該會對在web.xml中配置org.springframework.w

Java Web基礎(一)(HTML、Servlet/JSP)

       要成為牛逼的JavaWeb程式設計師, Java Web的基礎非常重要,現在有各種成熟的設計框架例如JQuery、Spring、Struts、Mybatis,將Java Web基礎的複雜

Java應用基礎微專業-入門-第1章用程序來做計算

version mac ear 浮點 spa class pin system font p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 20.0px "PingFang SC" } p.p2 { margin: 0.0px 0.

Java應用基礎微專業-工程

基礎 reason led tin user file ive named virus 第1章-命令行 1.1 命令行基礎 ls -a: list all files (including hidden files) .DS_Store: files detailed in

Java webservlet

詳細講解 get() 註意 des api 一個 剛才 分享 ima 入坑必備之ser

Java Web(一) Servlet詳解!!

註意 ont 道理 resource 通過 long values arr 客戶端請求     這篇文章到上一篇,距離的有點遙遠呀,隔了大概有兩個月把,中間在家過了個年,哈哈~ 現在重新開始拾起,最近在看一本個人覺得很棒的書,《Java Web 整合開發王者歸來》,現在寫的

Java Web(二) Servlet中response、request亂碼問題解決

nco post ima 處理 height http api img tle       三月不減肥,五月徒傷悲,這就是我現在的狀態,哈哈~ 健身、博客堅持。                               --WH 一、request請求參數出現的亂碼問題 

java web基礎學習 Forward和Redirect區別

響應 對象 一次 servlet t對象 資源 http 發的 請求 Forward和Redirect代表了兩種請求轉發方式:直接轉發和間接轉發。對應到代碼裏,分別是RequestDispatcher類的forward()方法和HttpServletRequest類的sen

java web 基礎 json 和 javaBean轉化

bubuko AI this set 實體 .json setname 自動 AS github地址: https://github.com/liufeiSAP/JavaWebStudy 實體類: package com.study.demo.domain; imp

JAVA Web基礎1

概念 網頁 serve 任務 webapps include指令 server HA lips 一.基本概念 1.C/S :client server 指客戶端服務 B/S :brower server 指瀏覽器服務,不用安裝, 只需要有一個服務器。 2.JSP基本概念

JAVA Web基礎2-JSP九大內置對象

上下文 inf AD 應對 版本號 提交 contex har post 在jsp開發中會頻繁使用到一些對象,如ServletContext HttpSession PageContext等.如果每次我們在jsp頁面中需要使用這些對象都要自己親自動手創建就會特別的繁瑣.SU

java Web基礎

項目 map 服務器端 獲取 ESS refresh url編碼 接收 取數 1. http協議: ftp協議: 2. 請求協議格式:        請求行       多個請求頭信息: 頭名稱 頭值 空行    

JAVA Web基礎4-過濾器與監聽器

javax 數組 patch param object servle on() .get 監聽器 一.過濾器1.定義:過濾器是指定義在服務器端的一段程序, 可以截獲客戶端發來的請求, 並根據一定規則進行過濾和攔截。2.過濾器的生命周期: 1.服務器啟動:先通過

java Web 工程servlet中@WebServlet("/HelloServlet")原理

多個 方便 ive http isp .org ada ado view 編寫好Servlet之後,接下來要告訴Web容器有關於這個Servlet的一些信息。在Servlet 3.0中,可以使用標註(Annotation)來告知容器哪些Servlet會提供服務以及額外信息

java webServlet生命周期

控制臺顯示 encoding port protect err 我們 sys 技術 vax 在Java web中 Servlet 是根基。雖然工作中幾乎沒人再去寫Servlet了,框架為我們完成了這些工作。我們只要專註於業務邏輯的實現。但是理解Servlet還是很有必要的。