Spring mvc原始碼分析系列--Servlet的前世今生

2022-09-28 21:08:31

Spring mvc原始碼分析系列--Servlet的前世今生

概述

上一篇文章Spring mvc原始碼分析系列--前言挖了坑,但是由於最近需求繁忙,一直沒有時間填坑。今天暫且來填一個小坑,這篇文章我們來說說Servlet的發展歷史。所以這篇文章還是比較輕鬆,不涉及太多的原始碼分析,簡單介紹Servlet的由來和發展。

Servlet是什麼

傳說在上世紀90年代,因為Internet和瀏覽器的飛速發展,使得基於瀏覽器的B/S模式隨之火爆發展起來。最初,使用者使用瀏覽器向WEB伺服器傳送的請求都是請求靜態的資源,比如html、css等。 但是可以想象:根據使用者請求的不同動態的處理並返回資源是理所當然必須的要求,例如使用者提交一些東西,伺服器就能按提交的內容反饋使用者不同的效果。所以人們應該非常迫切想要推出一項技術來實現動態的處理, java 為了應對上述需求,促進了servlet技術誕生。

Servlet 是在伺服器上執行的小程式。這個詞是在 Java applet的環境中創造的,Java applet 是一種當作單獨檔案跟網頁一起傳送的小程式,它通常用於在使用者端執行,結果得到為使用者進行運算或者根據使用者互作用定點陣圖形等服務。伺服器上需要一些程式,常常是根據使用者輸入存取資料庫的程式。這些通常是使用公共閘道器介面(Common Gateway Interface,CGI)應用程式完成的。然而,在伺服器上執行 Java,這種程式可使用 Java 程式語言實現。在通訊量大的伺服器上,JavaServlet 的優點在於它們的執行速度更快於 CGI 程式。各個使用者請求被啟用成單個程式中的一個執行緒,而無需建立單獨的程序,這意味著伺服器端處理請求的系統開銷將明顯降低。不清楚CGI是什麼?這篇文章CGI是什麼可以解答你的疑問。

Servlet與 CGI 比較存在的優點如下:

  • 與傳統的 CGI 和許多其他類似 CGI 的技術相比,Java Servlet 具有更高的效率,更容易使用,功能更強大,具有更好的可移植性,更節省投資。在未來的技術發展過程中,Servlet 有可能徹底取代 CGI。

  • 在傳統的 CGI中,每個請求都要啟動一個新的程序,如果 CGI 程式本身的執行時間較短,啟動程序所需要的開銷很可能反而超過實際執行時間。而在 Servlet 中,每個請求由一個輕量級的 Java 執行緒處理(而不是重量級的作業系統程序)。

  • 在傳統 CGI 中,如果有 N 個並行的對同一 CGI程式的請求,則該CGI程式的程式碼在記憶體中重複裝載了 N 次;而對於 Servlet,處理請求的是 N 個執行緒,只需要一份 Servlet 類程式碼。在效能優化方面,Servlet 也比 CGI 有著更多的選擇。

Servlet可以說是Java技術中最早的Web解決方案,Servlet與普通Java類的編寫非常類似。在Servlet中可以通過挨著行輸出Html等語句來實現頁面的樣式和輸出,資料的動態功能當然也就實現了。表現、邏輯、控制、業務全部混在Servlet類中。下面給出一個簡單例子來直觀感受一下。

public void doGet(HttpServletRequest request,HttpServletResponse)
   throws IOException,ServletException
{
    response.setContentType("text/html;charset=gb2312");
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head><title>Hello World!</title></head>");
    out.println("<body>");
    out.println("<p>Hello World!</p>");
    out.println("</body></html>");
}

Servlet是怎麼執行的

上一小節介紹到,Servlet是用於處理動態響應使用者端請求的。那麼Servlet是執行在哪裡的呢?

最早支援 Servlet 技術的是 JavaSoft 的 Java Web Server。此後,一些其它的基於 Java 的 Web Server 開始支援標準的 Servlet API。Servlet 的主要功能在於互動式地瀏覽和修改資料,生成動態 Web 內容。

還記得上一篇文章裡的靈魂拷問嗎? 瀏覽器的一個請求,是如何精確到達你的web伺服器裡的業務邏輯裡的,其中經歷的流程能說個所以然嗎 ,這個過程為:

  1. 使用者端傳送請求至伺服器端。
  2. 伺服器將請求資訊傳送至 Servlet。
  3. Servlet 生成響應內容並將其傳給伺服器。響應內容動態生成,通常取決於使用者端的請求。
  4. 伺服器將響應返回給使用者端。

以上的每一步都包含著大量的細節,現在廣泛使用的web伺服器是Tomcat,以Tomcat為例,簡單分析一下以上的四步:

  • 使用者端傳送請求至伺服器端。這部分涉及的是計算機網路的基礎知識,主要涉及各種協定,例如:ARP、DNS、TCP,HTTP等。
  • 伺服器將請求資訊傳送至 Servlet。這裡就涉及的是具體的web伺服器實現了,以Tomcat為例,這裡不展開細說,請求到達Tomcat後,會經過各種閥門的處理,然後最終進入到我們的Servlet裡面,這裡附上Tomcat的整體處理流程圖。

  • Servlet 生成響應內容並將其傳給伺服器。這部分沒啥好說,就是具體的業務邏輯。
  • 伺服器將響應返回給使用者端。跟第一點類似。

Servlet與Tomcat的關係

Tomcat是一個web伺服器,又有人稱其為Servlet容器,那麼顧名思義,Tomcat執行時會包含很多的Servlet在其中,當請求到達Tomcat時,Tomcat會幫我們將請求封裝成一個Request物件,經過不同層級的閥門處理後,轉發到了具體的Servlet裡。

所以可以看到二者的關係為:Servlet的執行依賴於Tomcat,Tomcat會為其提供很多基礎功能的支援。同時Tomcat對請求的業務處理是由具體的Servlet去實現,二者的結合有條不紊,實現了一個完整的web伺服器功能。

我們來看一下Servlet的發展歷史,可以看到Servlet的第一個版本釋出在1997年。

版本 日期 JAVA EE/JDK版本 特性
Servlet 4.0 2017年10月 JavaEE 8 HTTP2 [1]
Servlet 3.1 2013年5月 JavaEE 7 Non-blocking I/O, HTTP protocol upgrade mechanism
Servlet 3.0 2009年12月 JavaEE 6, JavaSE 6 Pluggability, Ease of development, Async Servlet, Security, File Uploading
Servlet 2.5 2005年10月 JavaEE 5, JavaSE 5 Requires JavaSE 5, supports annotation
Servlet 2.4 2003年11月 J2EE 1.4, J2SE 1.3 web.xml uses XML Schema
Servlet 2.3 2001年8月 J2EE 1.3, J2SE 1.2 Addition of Filter
Servlet 2.2 1999年8月 J2EE 1.2, J2SE 1.2 Becomes part of J2EE, introduced independent web applications in .war files
Servlet 2.1 1998年11月 未指定 First official specification, added RequestDispatcher, ServletContext
Servlet 2.0 JDK 1.1 Part of Java Servlet Development Kit 2.0
Servlet 1.0 1997年6月

再看Tomcat的發展歷史,可以看到Tomcat的第一個版本是晚於Servlet的,所以Tomcat也被認為是最早比較完善的對Servlet支援的web伺服器。

版本 日期 JAVA EE/JDK版本
tomcat-10 2021-06-16 JDK 11
tomcat-9 2015-11-19 JDK 1.8
tomcat-8 2013-08-05 JDK 1.7
tomcat-7 2010-06-13 JDK 1.6
tomcat-6 2006-10-21 JDK 1.5
tomcat-5 2004-08-29 JDK 1.4
tomcat-4 2003-09-06 JDK 1.3
tomcat-3 2003-09-06 JDK 1.1

再論Servlet是什麼

開啟程式碼,可以看到Servlet其實是一個介面,介面意味著什麼?意味著是規範,任何對它的合理實現都可以認為是一個Servlet,以我們常用的http為例,對http的支援是HttpServlet,看一下它的類繼承圖,可以看到它就是實現了Servlet介面。

簡單看一下Servlet介面定義的方法,可以看到只有五個方法,包含了初始化,執行業務邏輯,銷燬等重要過程。

其中重點的是service()方法。那麼這個方法是在哪裡被執行了呢?上面我們說過,Servlet是依賴於Tomcat執行的,所以這個方法應該是在Tomcat裡被呼叫了,我們看一下程式碼。

發現service()方法會在org.apache.catalina.core.ApplicationFilterChain#internalDoFilter(ServletRequest request, ServletResponse response)裡被呼叫。看到這裡,大家應該清楚Servlet如何跟Tomcat串聯起來了吧。

至於我們寫的Servlet是怎麼塞到了ApplicationFilterChain裡面,可以去看後續系列Tomcat的原理分析(又在挖坑,我直接好傢伙)。

小試牛刀

前面說了辣麼多,那Servlet專案是什麼結構,以及如何執行的,下面我們返璞歸真搞個簡單的Servlet專案來試試看。

新建一個專案,勾上。

過程省略,可參考文章,最終專案結構如下。

MyServlet程式碼如下。

/**
 * @author Codegitz
 * @date 2022/9/28 
 **/
@WebServlet({"/myServlet"})
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("invoke MyServlet#doGet() method");
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("invoke MyServlet#doPost() method");
        resp.getWriter().write("<h1>Hello World</h1>");
    }
}

啟動Tomcat就可以存取了。麻雀雖小五臟俱全,這就是一個簡單的Servlet專案構建過程。可以看到這個純粹的Servlet專案,沒有涉及到Spring mvc的東西,那麼如何涉及到Spring mvc後,專案會變成什麼樣呢?這個我們下一篇文章會介紹。

總結

這篇文章簡單介紹了一下Servlet的發展歷史,然後順帶簡單介紹了Tomcat的主要版本已經他們之間的關係。最後是簡單實現了一個Servlet,這裡還沒真正涉及到Spring mvc的內容。

下一篇就會真正的開始Spring mvc的分析,會簡單介紹一下mvc的發展歷史,隨後通過一個小demo引入,然後開始原始碼分析。

這篇文章太簡單了,you水一篇。哈哈。

如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。