上一篇文章Spring mvc原始碼分析系列--前言挖了坑,但是由於最近需求繁忙,一直沒有時間填坑。今天暫且來填一個小坑,這篇文章我們來說說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 技術的是 JavaSoft 的 Java Web Server。此後,一些其它的基於 Java 的 Web Server 開始支援標準的 Servlet API。Servlet 的主要功能在於互動式地瀏覽和修改資料,生成動態 Web 內容。
還記得上一篇文章裡的靈魂拷問嗎? 瀏覽器的一個請求,是如何精確到達你的web伺服器裡的業務邏輯裡的,其中經歷的流程能說個所以然嗎 ,這個過程為:
以上的每一步都包含著大量的細節,現在廣泛使用的web伺服器是Tomcat,以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,以我們常用的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水一篇。哈哈。
如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。