日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

通过源码详解 Servlet

發布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通过源码详解 Servlet 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Servlet 結構

1、Servlet

Servlet 該接口定義了5個方法。

  • init(),初始化 servlet 對象,完成一些初始化工作。它是由 servlet 容器控制的,該方法只能被調用一次
  • service(),接受客戶端請求對象,執行業務操作,利用響應對象響應客戶端請求。
  • destroy(),當容器監測到一個servlet從服務中被移除時,容器調用該方法,釋放資源,該方法只能被調用一次。
  • getServletConfig(),ServletConfig 是容器向 servlet 傳遞參數的載體。
  • getServletInfo(),獲取 servlet 相關信息。

Servlet 的生命周期:

1,初始化階段 調用 init() 方法

2,響應客戶請求階段  調用 service() 方法

3,終止階段  調用 destroy() 方法

在 Servlet 接口中的五個方法中涉及的接口有三個:ServletConfig 、 ServletRequest、 ServletResponse

這里先講講 ServletRequest 和 ServletResponse。

1)ServletRequest 由 Servlet 容器來管理,當客戶請求到來時,容器創建一個 ServletRequest 對象,封裝請求數據,同時創建一個 ServletResponse 對象,封裝響應數據。這兩個對象將被容器作為 service()方法的參數傳遞給 Servlet,Serlvet 利用 ServletRequest 對象獲取客戶端發來的請求數據,利用 ServletResponse 對象發送響應數據。

下面是 ServletRequest 中所有的方法,根據方法名大概就可以猜到這些方法到底是干啥用的。

2)ServletResponse 發送響應數據

2、ServletConfig

ServletConfig 是容器向 servlet 傳遞參數的載體。

ServletConfig的4個常用方法:

1)public String getInitParameter(String name):返回指定名稱的初始化參數值;

2)public Enumeration getInitParameterNames():返回一個包含所有初始化參數名的 Enumeration 對象;

3)public String getServletName():返回在 DD 文件中<servlet-name>元素指定的 Servlet 名稱;

4)public ServletContext getServletContext():返回該 Servlet 所在的上下文對象;

這里詳細講下 ServletContext :

Servlet 上下文對象(ServletContext):每個Web應用程序在被啟動時都會創建一個唯一的上下文對象,Servlet 可通過其獲得 Web 應用程序的初始化參數或 Servlet 容器的版本等信息,也可被 Servlet 用來與其他 Servlet 共享數據。

1、獲得 ServletContext 應用:

(1)、直接調用 getServletContext()方法

ServletContext context = getServletContext();

(2)、使用 ServletConfig 應用,再調用它的 getServletContext()方法

ServletContext context = getServletConfig.getServletContext();

2、獲得應用程序的初始化參數:

(1)、public String getInitParameter(String name):返回指定參數名的字符串參數值,沒有則返回 null;

(2)、public Enumeration getInitParameterNames():返回一個包含多有初始化參數名的 Enumeration 對象;

3、通過 ServletContext 對象獲得資源

(1)、public URl getResource(String path):返回由給定路徑的資源的 URL 對象,以 “/” 開頭,為相對路徑,相對于Web 應用程序的文檔根目錄;

(2)、public InputStream getResourceAsStream(String path):從資源上獲得一個 InputStream 對象,等價于getResource(path).oprenStream();

(3)、public String getRealPath(String path):返回給定的虛擬路徑的真實路徑;

4、登陸日志:使用 log()方法可以將指定的消息寫到服務器的日志文件中

(1)、public void log(String msg):參數 msg 為寫入日志文件消息

(2)、public void log(String msg,Throwable throwable):將 msg 指定的消息和異常的棧跟蹤信息寫入日志文件

5、使用 RequestDispatcher 實現請求轉發

(1)、RequestDispatcher getRequestDiapatcher(String path):必須以 “/“ 開頭相對于應用程序根目錄,而ServletRequest 可以傳遞一個相對路徑

(2)、RequestDipatcher getNamedDiapatcher(String name):參數 name 為一個命名的 Servlet 對象

6、使用 ServletContext 對象存儲數據

(1)、public void serAttribute(String name,Object object):將給定名稱的屬性值對象綁定到上下文對象上;

(2)、public Object getAttribute(String name):返回綁定到上下文對象的給定名稱的屬性值;

(3)、public Enumeration getAttributeNames():返回綁定到上下文對象上的所有屬性名的 Enumeration 對象;

(4)、public void removeAttribute(String name):刪除綁定到上下文對象指定名稱的屬性;

ServletRequest 共享的對象僅在請求的生存周期中可以被訪問;

HttpSession 共享的對象僅在會話的生存周期中可以被訪問;

ServletContext 共享的對象在整個 Web 應用程序啟動的生存周期中可以被訪問;

7、檢索 Servlet 容器的信息

(1)、public String getServletInfo():返回 Servlet 所運行容器的名稱和版本;

(2)、public int getMajorVersion():返回容器所支持的 Servlet API 的主版本號;

(3)、public int getMinorVersion():返回容器所支持的 Servlet API 的次版本號;

(4)、public String getServletContext():返回 ServletContext 對應的 web 應用程序名稱?<display-name>元素定義的名稱;

3、GenericServlet 抽象類

GenericServlet 定義了一個通用的,不依賴具體協議的 Servlet,它實現了 Servlet 接口和 ServletConfig 接口,它的方法在文章的第一張圖就給出了。

4、HttpServlet 抽象類

4.1、HTTP 請求方式

  • GET : 獲取由請求 URL 標識的資源
  • POST : 向 Web 服務器發送無限制長度的數據
  • PUT : 存儲一個資源到請求的 URL
  • DELETE : 刪除由 URL 標識的資源
  • HEAD : 返回 URL 標識的頭信息
  • OPTIONS : 返回服務器支持的 HTTP 方法
  • TRACE : 返回 TRACE 請求附帶的頭字段
  • 4.2、對應的服務方法:

  • doGet() : 調用服務器的資源, 并將其作為響應返回給客戶端. doGet() 調用在 URL 里顯示正在傳送給 Servlet 的數據,這在系統的安全方面可能帶來一些問題, 比如說, 用戶登錄時, 表單里的用戶名和密碼需要發送到服務器端, doGet() 調用會在瀏覽器的 URL 里顯示用戶名和密碼.
  • doPost() : 它用于把客戶端的數據傳給服務端, 使用它可以以隱藏方式給服務器端發送數據. Post 適合發送大量數據.
  • doPut() : 調用和 doPost() 相似, 并且它允許客戶端把真正的文件存放在服務器上, 而不僅僅是傳送數據.
  • doDelete() : 它允許客戶端刪除服務器端的文件或者 Web 頁面.它的使用非常少.
  • doHead() : 它用于處理客戶端的 Head 調用,并且返回一個 response. 當客戶端只需要響應的 Header 時,它就發出一個Header 請求.這種情況下客戶端往往關心響應的長度和響應的 MIME 類型.
  • doOptions(): 它用于處理客戶端的 Options 調用,通過這個調用, 客戶端可以獲得此 Servlet 支持的方法.如果 Servlet 覆蓋了 doPost() 方法, 那么將返回: Allow: POST, TRACE, OPTIONS, HEAD
  • doTrace:處理 TRACE 請求
  • 4.3、SERVLET SERVICE 方法詳解

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    public void service(ServletRequest req, ServletResponse res)

    throws ServletException, IOException

    {

    HttpServletRequest request;

    HttpServletResponse response;

    // 如果傳入的 HTTP 請求和 HTTP 響應不是 HTTP 的領域模型,則拋出 Servlet 異常,這個異常會被 Servlet 容器所處理

    if (!(req instanceof HttpServletRequest &&

    res instanceof HttpServletResponse)) {

    throw new ServletException("non-HTTP request or response");

    }

    // 既然是 HTTP 協議綁定的 Serlvet, 強制轉換到 HTTP 的領域模型

    request = (HttpServletRequest) req;

    response = (HttpServletResponse) res;

    // 如果傳入的請求和響應是預期的 HTTP 請求和 HTTP 響應,則調用 HttpServlet 的 service() 方法。

    service(request, response);

    }

    4.4、HTTPSERVLET SERVICE 方法詳解

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    protected void service(HttpServletRequest req, HttpServletResponse resp)

    throws ServletException, IOException

    {

    // 從 HTTP 請求中取得這次請求所使用的 HTTT 方法

    String method = req.getMethod();

    // 如果這次請求使用 GET 方法

    if (method.equals(METHOD_GET)) {

    // 取得這個 Servlet 的最后修改的時間

    long lastModified = getLastModified(req);

    if (lastModified == -1) {

    // servlet doesn't support if-modified-since, no reason

    // to go through further expensive logic

    //-1 代表這個 Servlet 不支持最后修改操作,直接調用 doGet() 進行處理 HTTP GET 請求

    doGet(req, resp);

    } else {

    // 如果這個 Servlet 支持最后修改操作,取得請求頭中包含的請求的最后修改時間

    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);

    if (ifModifiedSince < lastModified) {

    // If the servlet mod time is later, call doGet()

    // Round down to the nearest second for a proper compare

    // A ifModifiedSince of -1 will always be less

    // 如果請求頭中包含的修改時間早于這個 Servlet 的最后修改時間,說明這個 Servlet 自從客戶上一次 HTTP 請求已經被修改了 , 設置最新修改時間到響應頭中

    maybeSetLastModified(resp, lastModified);

    // 調用 doGet 進行進行處理 HTTP GET 請求

    doGet(req, resp);

    } else {

    // 如果請求頭中包含修改時間晚于這個 Servlet 的最后修改時間,說明這個 Servlet 自從請求的最后修改時間后沒有更改過,這種情況下,僅僅返回一個 HTTP 響應狀態 SC_NOT_MODIFIED

    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

    }

    }

    } else if (method.equals(METHOD_HEAD)) {

    // 如果這次請求使用 HEAD 方法

    // 如果這個 Servlet 支持最后修改操作,則設置這個 Servlet 的最后修改時間到響應頭中

    long lastModified = getLastModified(req);

    maybeSetLastModified(resp, lastModified);

    // 和對 HTTP GET 方法處理不同的是,無論請求頭中的修改時間是不是早于這個 Sevlet 的最后修改時間,都會發 HEAD 響應給客戶,因為 HTTP HEAD 響應是用來查詢 Servlet 頭信息的操作

    doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {

    // 如果這次請求使用 POST 方法

    doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {

    // 如果這次請求使用 PUT 方法

    doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {

    // 如果這次請求使用 DELETE 方法

    doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {

    // 如果這次請求使用 OPTIONS 方法

    doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {

    // 如果這次請求使用 TRACE 方法

    doTrace(req,resp);

    } else {

    // Note that this means NO servlet supports whatever

    // method was requested, anywhere on this server.

    // 如果這次請求是其他未知方法,返回錯誤代碼 SC_NOT_IMPLEMENTED 給 HTTP 響應,并且顯示一個錯誤消息,說明這個操作是沒有實現的

    String errMsg = lStrings.getString("http.method_not_implemented");

    Object[] errArgs = new Object[1];

    errArgs[0] = method;

    errMsg = MessageFormat.format(errMsg, errArgs);

    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);

    }

    }

    5、Servlet 的多線程問題

    1、當涉及到 Servlet 需要共享資源是,需保證 Servlet 是線程安全的

    2、注意事項:

    (1)、用方法的局部變量保持請求中的專有數據;

    (2)、只用 Servlet 的成員變量來存放那些不會改變的數據;

    (3)、對可能被請求修改的成員變量同步(用 Synchronized 關鍵字修飾);

    (4)、如果 Servlet 訪問外部資源,那么需要同步訪問這些資源;

    3、實現 SingleThreadModel 接口的 Servlet 在被多個客戶請求時一個時刻只能有一個線程運行,不推薦使用。

    4、如果必須在 servlet 使用同步代碼,應盡量在最小的范圍上(代碼塊)進行同步,同步代碼越少,Servlet 執行才能越好,避免對 doGet() 或 doPost() 方法同步。

    總結

    全文首先通過一張 Servlet 中的核心 Servlet 類圖關系,了解了幾種 Servlet 之間的關系及其內部方法。然后在分別介紹這幾種 Servlet,通過分析部分重要方法的源碼來了解,還介紹了 Servlet 中多線程的問題的解決方法。

    注:文章原創,首發于:zhisheng 的博客,文章可轉載但請注明地址為:http://www.54tianzhisheng.cn/2017/07/09/servlet/

    總結

    以上是生活随笔為你收集整理的通过源码详解 Servlet的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。