Java後端自頂向下方法——Servlet規範

Java後端自頂向下方法——Servlet規範

(一)簡單介紹

Servlet是啥?Servlet是Sun公司提供的一門用於開發動態web資源的技術,按照一種約定俗成的稱呼習慣,通常我們也把實現了Servlet接口的java程序,稱之爲Servlet。但是實際上,真正意義上的Servlet只是一個接口而已,在javax.servlet包中,並沒有實現。我們來看看Servlet所在的層級。
在這裏插入圖片描述
很顯然,Servlet位於Service之前,HTTP服務器之後,主要的工作就是處理HTTP請求(注意:不是接收HTTP請求!),然後調用對應的Service進行業務邏輯處理,再將處理結果返回。這就是Servlet的處理過程的概括,接下來我們來詳細講講他的具體過程。

(二)Servlet工作流程

在講工作流程之前,我們先來看看Servlet接口中定義了哪些方法。

public interface Servlet {
    void init(ServletConfig config) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}

是的,如你所見,這個接口只有五個方法。其中init、service、destroy方法分別對應了service的三個生命週期。另外兩個方法一個是獲取配置,一個是獲取信息。那麼我們先來看看Servlet的生命週期吧。

在這裏插入圖片描述
其實Servlet的生命週期非常好理解,一共就三個,就像我們人的生命週期一樣,分爲出生、工作、死亡。其中最重要的當然是service部分,因爲這也是Servlet實現自己價值的地方,在這裏Servlet會將HTTP請求進行相應的處理,具體的處理方法在後面會講。看完了生命週期我們再來看Servlet工作流程。
在這裏插入圖片描述
當客戶請求某一個資源時,HTTP服務器會用一個HttpServletRequest對象把客戶的請求信息封裝起來,然後調用Servlet容器的service方法,Servlet容器拿到請求後,根據請求的URL和Servlet的映射關係,找到相應的Servlet(如果Servlet還沒有被加載,就用反射機制創建這個Servlet,並調用Servlet的init方法來完成初始化)。接着,調用Servlet的service方法來處理請求,把HttpServletResponse對象返回給HTTP服務器,HTTP服務器會把響應發送給客戶端。

由此可見,Servlet所做的工作其實非常簡單,下面我給出一張時間軸圖,更加清晰的展現工作流程和各個對象的實例化的時間。這張圖請大家一定要熟記!!!這裏最需要注意的是HttpServletRequest和HttpServletResponse對象的實例化時間,他們都是在調用Servlet之前創建的(非常重要!!!後面會用到)。

在這裏插入圖片描述
你可能會覺得疑惑,圖裏這個HttpServlet是啥?這和Servlet有什麼關係?實際上,這個HttpServlet其實是Servlet接口的一個基於HTTP協議的實現類,我們接下來會詳細介紹他。不過在此之前,我們先講講上面這張圖中包含的一個重要的東西——ServletContainer。

(三)Servlet容器

Servlet容器用來部署Web應用,提供對於Servlet生命週期的管理。總而言之,Servlet容器就是用來裝Servlet的(通俗易懂)。對了,在講Servlet容器之前,最好還是先講一下Web服務器。

Web服務器使用HTTP協議來傳輸數據。最簡單的一種情況是,用戶在瀏覽器(中輸入一個URL,然後就能獲取網頁。因此,服務器完成的工作就是發送網頁至客戶端。傳輸過程遵循HTTP協議,它指明瞭請求消息和響應消息的格式。這是傳統開發模式下的流程,在前後端分離的模式下,服務器更多的是將客戶端請求的數據發送給客戶端,而不是發送整個網頁。其實,瀏覽器我們每天都在接觸,服務器雖然我們可能接觸不到,但是他們每天都在爲我們提供服務,我們對他們的功能肯定也不會陌生到哪裏去。

接下來看看Servlet容器。Servlet容器也叫做Servlet引擎,是Web服務器的一部分。Servlet沒有自己的main方法,不能獨立運行,它必須被部署到Servlet容器中,由容器來實例化和調用 Servlet的方法,Servlet容器在Servlet的生命週期內包容和管理Servlet。那麼我們爲什麼一定要用Servlet容器,或者說用了之後有什麼好處呢?

  1. 通信支持:利用容器提供的方法,你能輕鬆的讓Servlet與Web服務器對話,而不用自己建立serversocket、監聽某個端口、創建流等。容器知道自己與web服務器之間的協議,所以你的Servlet不用擔心Web服務器和你自己的web代碼之間的API,只需要考慮如何在Servlet中實現業務邏輯。
  2. 生命週期管理:Servlet容器控制着Servlet的生與死,它負責加載類、實例化和初始化Servlet,調用Servlet方法,以及使servlet實例被垃圾回收,有了Servlet容器,你不需要太多的考慮資源管理。
  3. 多線程支持:容器會自動爲它所接收的每個Servlet請求創建一個新的java線程。針對用戶的請求,如果Servlet已經運行完相應的HTTP服務方法,這個線程就會結束。但我們並不是完全不需要考慮線程安全性,同步問題依舊會遇到,不過這樣至少能少做很多工作。

這些術語看不懂關係不大,你只需要知道,Servlet容器的出現使我們不用再關心底層的通信和線程安全,我們可以把自己的注意力只放在處理業務邏輯上,簡化開發步驟。後面我們就來介紹如何書寫自己的Servlet。

(四)Servlet繼承體系

在這裏插入圖片描述
Servlet的繼承體系稍微有一點複雜,我們就從上往下講。

首先我們先看最上層的兩個接口,一個是ServletConfig接口,一個是我們熟悉的Servlet接口。ServletConfig接口主要負責Servlet的配置信息,常用來在Servlet初始化時進行信息傳遞。這個不算特別重要,我們初學的時候應該先把注意力放在業務邏輯上。我們再來看Servlet接口,裏面就是我們熟悉的五個方法,所以,接下來我們肯定是要依次實現他們。

然後我們來看這個GenericServlet。GenericServlet是個抽象類,所以他無法構造Servlet對象,必須子類實現裏面所有的抽象方法才能實例化對象。他的功能是給出了設計Servlet的一些骨架,定義了Servlet生命週期,還有一些得到名字、配置、初始化參數的方法,其設計的是和應用層協議無關的,也就是說你有可能用非HTTP協議實現它。我們只需要記住,這個GenericServlet只是一個骨架,我們還需要另外的具體實現。

接着就是我們最重要的HttpServlet類。HttpServlet是GenericServlet的子類,具有GenericServlet的一切特性,還額外添加了doGet, doPost, doDelete, doPut, doTrace等方法對應處理HTTP協議裏的命令的請求響應過程,我們在開發時最重要的就是實現這些方法來處理不同的HTTP請求。我們可以說HttpServlet是基於HTTP協議的Servlet實現類,當然還有基於別的協議的Servlet實現類,因爲用的不多所以我也沒有去太多瞭解。一般沒有特殊需要,我們自己寫的Servlet都擴展自HttpServlet 。最底部的MyServlet就是我們自己書寫的Servlet,用來實現我們自己項目的業務邏輯。下面我給出一張繼承簡圖:

在這裏插入圖片描述
是的,大名鼎鼎的Servlet的結構就是這麼簡單,所以我們學東西的時候不能有畏難情緒。那些你可能完全聽不懂的名詞或者句子其實仔細想想可能也沒有那麼難吧。

(五)Servlet實戰

講了那麼多理論知識,我們終於可以用Servlet做些事情了。因爲我們還沒有講Tomcat(一種Web服務器,也是一種Servlet容器)的配置,沒有Tomcat我們的Servlet是無法運行的,所以在這裏還是看我演示就行。到Tomcat章節我會告訴大家如何配置Tomcat。

在講實戰之前我先來介紹一個調試工具——postman。這是一款專門用來調試API的工具,功能全面、界面美觀、操作簡便。我們後面調試Servlet時都靠他了,大家可以去Google一下然後安裝一個。

話不多說,我們先寫一個簡單的Servlet:

package controller;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LoginController", value = "/entrance/login")
public class LoginController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println(request.getMethod());
        System.out.println(request.getParameter("userName") + request.getParameter("password"));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

我們來看一下這段代碼幹了什麼事,首先我們在註解裏面標明瞭這個Servlet的名字和對應的虛擬路徑。(注意:這邊只是虛擬路徑,請求的參數不需要寫在這裏)然後我們重寫了兩個方法,分別是doPost和doGet,這兩個方法分別處理請求到“/entrance/login”路徑下的post方式請求和get方式請求。這兩個方法的參數是一模一樣的,都是HttpServletRequest對象和HttpServletResponse對象,這兩個對象來自Tomcat中的HTTP服務器,封裝了請求和響應。如果忘記了可以去看看上文中的圖(Servlet執行流程)。

既然我們已經拿到了HttpServletRequest對象,那麼接下來的事就簡單了,我們可以調用HttpServletRequest中的各種方法,拿到請求中的任何信息。比如請求頭、請求體、請求參數、請求方式、cookie等(總之無論你的請求裏有啥,他都能拿得到)。在這裏我演示了一下拿請求方式和請求參數。我們用postman工具請求一下他。

在這裏插入圖片描述
顯然,控制檯打印出了請求方式爲POST,請求參數爲123和456。經過了這個案例,你應該明白了Servlet是怎麼拿到我們的請求中的信息的。哦對了有一點忘記說了,你一定發現了我的doGet方法中又寫了一個doPost方法,這是幹什麼的?很簡單,當用get方式請求時,這個doGet方法會調用doPost方法,相當於是將請求又轉交給了doPost方法。因此,在這個案例中,你用get方式和post請求這個路徑,效果是完全一樣的(當然,控制檯打印的請求方式肯定是不一樣的)。但是,如果你用DELETE、PUT等其他的方式請求,很顯然是沒用的。如果你需要用到這些方式,你必須要重寫doDelete、doPut等方法。下面放一張我分別用post方式和get方式請求之後控制檯的結果:
在這裏插入圖片描述
這個結果是不是和你想象的一樣,如果一樣的話,說明你已經明白了Servlet的基本用法。至於HttpServletRequest的其他方法和HttpServletResponse中的方法,你可以通過Google或者查文檔或者閱讀源碼的方式來學習。我會在Servlet源碼分析章節中細講。

2020年4月23日

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章