Servlet是什么
servlet本質上是一個接口,它壓根就不管也管不著哪些網絡協議,如:http。
那servlet到底是干什么的呢?很簡單,接口的作用是什么?規范唄!
servlet接口定義的是一套處理網絡請求的規范,所有實現servlet的類,都需要實現它的那五個方法,其中最主要的是兩個聲明周期方法init()和destory(),還有一個處理請求的service()。也就是說,所有實現servlet接口的類,或者說,所有想要處理網絡請求的類,都需要回答這三個問題:
- 你初始化時要做什么?
- 你銷毀時要做什么?
- 你接收到請求時要做什么?
這是Java給的一種規范!就像阿西莫夫的機器人三大定律、行尸走肉里Rick的那三個問題一樣,規范!
servlet是一個規范,那實現了servlet的類,就能處理請求了嗎?
不能。你從來不會在servlet中寫什么監聽8080端口的代碼,servlet不會直接和客戶端打交道!
那請求是怎么來到servlet的呢?答案是servlet容器,比如我們最常用的Tomcat。servlet都是部署在一個容器中的,不然你的servlet根本不起作用。
Tomcat才是與客戶端直接打交道的家伙,它監聽了端口,請求過來后,根據URL等信息,確定要將請求交給哪個servlet去處理,然后調用那個servlet的service方法,service方法返回一個response對象,Tomcat再把這個respond返回給客戶端。
Servlet本身在Tomcat中是“非常被動”的一個角色,處理的事情也很簡單。網絡請求與響應,不是它的主要職責,它其實更偏向于業務代碼。所謂的request和respond是Tomcat傳給它,用來處理請求和響應的工具,但它本身不處理這些。
Servlet的前世今生
所謂Tomcat其實是Web服務器和Servlet容器的結合體。
(1) Web服務器的所做的工作本質上是:
將某個主機上的資源映射為一個URL供外界訪問
(2) 那什么是servlet容器呢?
servlet容器,顧名思義里面存放的是servlet對象。
我們為什么能通過web服務器映射的URL訪問到資源呢?肯定需要寫程序處理請求,主要三個過程
接收請求(收到請求)
處理請求(處理請求)
響應請求(返回處理結果)
任何一個應用程序,必然包括這三個步驟。其中接收請求和響應請求是共性功能,而且沒有差異性。于是,就可以把接收和響應兩個步驟抽取成web服務器。
但處理請求的邏輯是不同的。沒關系,抽取出來做成servlet,交給程序員自己編寫
當然,隨著互聯網的發展,出現了三次架構,所以一些邏輯就從servlet抽取出來,分擔到service和dao
但是servlet并不擅長往瀏覽器輸出HTML頁面,所以就出現了JSP。
等Spring家族出現后,Servlet開始退居幕后,取而代之的是SpringMVC。SpringMVC的核心組件DispacterServlet其實本質是一個Servlet,但是它已經自立門戶,在原來HttpServlet的基礎上,有封裝了一層邏輯。
Servlet工作模式
Servlet(Server Applet,服務端小程序,是服務端的一小部分),全稱Java Servlet,未有中文譯文。是用Java編寫的服務器端程序。其主要功能在于交互式地瀏覽和修改數據,生成動態Web內容。狹義的Servlet是指Java語言實現的一個接口,廣義的Servlet是指任何實現了這個Servlet接口的類,一般情況下,人們將Servlet理解為后者。
Servlet運行于支持Java的應用服務器中。從原理上講,Servlet可以響應任何類型的請求,但絕大多數情況下Servlet只用來擴展基于HTTP協議的Web服務器。
最早支持Servlet標準的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服務器開始支持標準的Servlet。
工作模式:
1、客戶端請求該 Servlet;
2、Tomcat加載 Servlet 類到內存;
3、Tomcat實例化并調用init()方法初始化該 Servlet;
4、Tomcat調用service()(根據請求方法不同調用doGet() 或者 doPost(),此外還有doHead()、doPut()、doTrace()、doDelete()、doOptions());
5、destroy();
6、加載和實例化 Servlet。這項操作一般是動態執行的。然而,Server 通常會提供一個管理的選項,用于在 Server 啟動時強制裝載和初始化特定的 Servlet;
7、Server 創建一個 Servlet的實例;
8、第一個客戶端的請求到達 Server;
9、Server 調用 Servlet 的 init() 方法(可配置為 Server 創建 Servlet 實例時調用,在 web.xml 中 <servlet> 標簽下配置 <load-on-startup> 標簽,配置的值為整型,值越小 Servlet 的啟動優先級越高);
10、一個客戶端的請求到達 Server;
11、Server 創建一個請求對象,處理客戶端請求;
12、Server 創建一個響應對象,響應客戶端請求;
13、Server 激活 Servlet 的 service() 方法,傳遞請求和響應對象作為參數;
14、service() 方法獲得關于請求對象的信息,處理請求,訪問其他資源,獲得需要的信息;
15、service() 方法使用響應對象的方法,將響應傳回Server,最終到達客戶端。service()方法可能激活其它方法以處理請求,如 doGet() 或 doPost() 或程序員自己開發的新的方法;
16、對于更多的客戶端請求,Server 創建新的請求和響應對象,仍然激活此 Servlet 的 service() 方法,將這兩個對象作為參數傳遞給它。如此重復以上的循環,但無需再次調用 init() 方法。一般 Servlet 只初始化一次(只有一個對象),當 Server 不再需要 Servlet 時(一般當 Server 關閉時),Server 調用 Servlet 的 destroy() 方法。
Servlet是J2EE 規范中的一種,主要是為了擴展java作為web服務的功能。J2EE 從92年的J2EE 1.2到現在J2EE8 從12個規范到現在20多個規范,越來越完善。他的作用就是為java程序提供一個統一的web應用的規范,方便程序員統一的使用這種規范來編寫程序(開發人員按規范寫程序),應用容器可以使用提供的規范來實現自己的特性(容器按規范調用程序)。比如tomcat的代碼和jetty的代碼就不一樣是吧,但作為程序員你只需要了解servlet規范就可以從request中取值,你可以操作session等等。不用在意應用服務器底層的實現的差別而影響你的開發。
當然你也可以自己寫一個http 服務器,自己定義一套API,比如你在底層接受到一個http請求后,你把這個http請求的header、cookie和param等封裝成一個MyRequest.class 。然后你要得到,你在你的MyServlet中從MyRequest對象中拿到param請求參數,校驗成功后需要返還給瀏覽器一個HTTP response。其中必須要有一個session,所以你往cookie中寫了一個字段,LAOZIDESESSIONID=878361839QWQWEQEQE,同時把這個sessionid放在了自己的內存中。下一次瀏覽器再訪問你就會帶上這個LAOZIDESESSIONID這個cookie,你就知道他原來已經訪問過了,而且上一次訪問的數據你都有(在第一次保存在內存中)
但是有沒有想過,如果每個程序員都寫一個自己的HTTP服務器,該程序員離職了咋辦。而且你用你的,我用我的,遇到問題都不能一起解決,你一會兒只支持http/1.0 ,別人都支持http/2.0了(雖然這個是在底層的實現了,和servlet沒半毛錢關系,大家注意了,打個比方而已)。別人都支持注解了,你還在寫配置呢!肯定不能啊,所有J2EE要出一個規范,要管住你們這群人,大家都要同步走。大家都用我這套規范,所有的請求都放在Request中,返回都放在response中。sessionID的名稱也都可以自己設置,比如tomcat你可以<Context sessionCookieName="zheshilaozidesessionid" >
講了這么多廢話,總結來說Servlet就是一群人來制定java應用中使用web時的各種規范,統一接口,其他內部實現由廠商自己實現,tomcat jetty jboss等等應運而生。面向接口編程!!很熟悉吧
關于他如何工作的:一個http請求到來,容器將請求封裝成servlet中的request對象,在request中你可以得到所有的http信息,然后你可以取出來操作,最后你再把數據封裝成servlet的response對象,應用容器將respose對象解析之后封裝成一個http response。
web服務器習慣處理靜態頁面,所以需要一個程序來幫忙處理動態請求(如當前時間)。Web服務器程序會將動態請求轉發給幫助程序,幫助程序處理后,返回處理后的靜態結果給web服務器程序。這樣就避免了web服務器程序處理動態頁面。Servlet的本質是一個幫助程序。如下圖
Servlet工作流程分為三個階段。init(初始化),service(運行),destroy(銷毀)
Servlet沒有main方法,所有行為由Container控制。Container就是一個java程序。
在加載Servlet的.class后,Servlet會由構造函數生成一個實例,然后Container調用init()方法完成參數的初始化,接著調用service()方法,service會根據網頁的請求,調用doGet或者doPost方法,最后調用銷毀方法。整個流程如下圖:
如何編寫一個Servlet
查看接口方法:
五個方法,最難的地方在于形參,然而Tomcat會事先把形參對象封裝好傳給我們。除此之外,既不需要我們寫TCP連接,也不需要我們解析HTTP請求,更不需要我們把結果轉換成HTTP響應,request對象和response對象幫我們搞定了。
在Servlet里面主要寫的代碼都是業務邏輯,和原始的、底層的解析、連接等沒有絲毫關系。最難的幾個操作,人家已經給你封裝成形參傳進來了。
也就是說servlet雖然是個接口(接口保證了方法名的規范性和一致性,名字不一致,tomcat將無法通過名稱進行調用),但是實現類只是個空殼,我們寫點業務邏輯就好了
總的來說,Tomcat已經幫我們完成了底層的操作,并且傳入了三個對象:ServletConfig、ServletRequest、ServletResponse。
ServletConfig
ServletConfig即“servlet配置”,就是我們在web.xml中配置的servlet。它封裝了servlet的一些參數信息。如果需要,我們可以從它獲取。
Tomcat解析web.xml。通過<url-pattern>找到<servlet-name>,然后通過<servlet-name>找到<servlet-class>,然后通過java的反射機制將class實例化為對象。
Request/Response
這是接收請求和發送請求的類,Tomcat已經處理并封裝好了,不需要servlet操心。HTTP請求到達Tomcat之后,Tomcat通過字符串解析,把各個請求頭(Header)、請求地址(URL)、請求參數等都封裝進Request對象中。通過調用
等方法,就可以得到瀏覽器當初發送的請求信息。
至于Response,Tomcat傳給Servlet時,它還是空的對象。Servlet邏輯處理后得到結果,最終通過response.write()方法,將結果寫入response內部的緩沖區。Tomcat會在servlet處理結束后,拿到response,遍歷里面的信息,組裝成HTTP響應發給客戶端。
Servlet接口5個方法,其中init、service、destory是聲明周期方法。init和destory各自只執行一次,即servlet創建和銷毀時。而service會在每次有新請求到來時被調用。也就是說,我們主要的業務代碼需要寫在service中。?
但是,瀏覽器發送請求基本的有兩種:GET/POST。于是我們必須這樣寫
那能不能簡化呢?
于是我們發現了一個GenericServlet,是個抽象類。(Generic:通用的)
作用
- 提升了init方法中原本是形參的servletConfig對象的作用域,方便其他方法使用
- init方法中還調用了一個init空參方法,如果我們希望在servlet創建時做一些什么初始化操作,可以繼承GenericServlet后,覆蓋init空參方法
- 由于需要其他方法也可以使用servletConfig,于是寫了一個getServletConext。
但是service()方法沒有實現。
于是又引入了一個HttpServlet
它繼承了GenericServlet。
GenericServlet本身是一個抽象類,有一個抽象方法service。實現如下:
也就是說,HttpServlet的service方法已經替我們完成了復雜的請求方法判斷。
問題是HttpServlet為什么要聲明成抽象類呢?它的文檔中注釋了:
一個類聲明成抽象方法,一般有兩個原因:
- 有抽象方法
- 沒有抽象方法,但是不希望被實例化
HTTPServlet做成抽象類,僅僅是為了不讓new
它為什么不希望被實例化,而且要求子類重寫doGet、doPost等方法呢?
我們來看一下源碼:
如果我們沒有重寫會怎么樣?
瀏覽器頁面會顯示405(http.method_get_not_supported)
也就是說,HttpServlet雖然在service中幫我們寫了請求方式的判斷。但是針對每一種請求,業務邏輯代碼都是不同的,HttpServlet無法知曉子類想干嘛,所以就抽象出了7個方法,并且提供了默認實現:報405、400錯誤,提示請求不支持。
但是這種實現本身非常。。。簡單來說等于沒有。所以不能讓它被實例化,不然調用doXXX方法是無用功。
這就是模板方法模式:父類把能寫的邏輯都寫完,把不確定的業務代碼抽象成一個方法,調用它。當子類重寫該方法,整個業務代碼就活了
子類繼承父類的service方法,tomcat直接調用service方法。子類覆蓋父類的doXxx方法,子類繼承的service優先調用子類的doXxx方法。(java子類調用父類的方法的步驟:1、子類的對象調用方法時,會首先在子類中查找,如果子類中沒有該方法,再到父類中查找;2、如果該方法中又調用了其他方法,那么還是按照之前的順序,先在子類中查找,再在父類中查找。)
下面是我曾經做過的Servlet筆記
Servlet體系結構
servlet首先是一個接口,GenerivServlet實現了這個接口。
他們都是屬于javax.servlet.*;的這個包里面的
此外,在javax.servlet.http*;的這個包里面呢,其實也含有一個重要的servlet類叫httpServlet
首先我們看到ie瀏覽器,我們講servlet也好,講jsp也罷,首先我們要明確的是這是一個bs的體系結構,所以說他必然有瀏覽器的,那么瀏覽器要去訪問Tomcat其中的某一個servlet或者說是jsp的時候呢,必須在ie瀏覽器當中輸入
http://........
之類的命令敲了回車鍵之后瀏覽器就會向你指定的Tomcat發送http請求的(向指定的主機的指定端口發出http請求),這個請求會被Tomcat的Web服務器的這個模塊接收,web服務器處理之后會轉發給Tomcat的容器部分進行處理,他會幫助ie瀏覽器找到這個請求想要找到的servlet,這時在容器里面的行為要么是在容器內再向數據庫發送操作數據庫的命令,要么呢直接返回結果 ,其實就是靜態的html頁面,當頁面被web服務器模塊接收到之后,他會將靜態頁面返回給那個發送請求的那個ie瀏覽器,ie瀏覽器得到這個結果之后,就會自己顯示出來,然后用戶就能看到結果,所以可以將我們的結果分成三個部分,第一個部分是ie瀏覽器,第二個是Tomcat模塊,第三個就是數據庫模塊。
其實開發servlet有三種方法,一種是實現servlet接口,一種是繼承GernericServlet,一種是繼承HttpServlet。 為什么會有三種方法呢?因為這個servlet這個技術并不是說一出現之后就是成熟的,他經歷過這三個發展階段,因此它具有這三種方式,其實在最先前是實現servlet接口方法來開發的,后面的時候技術人員發現這樣寫似乎不太方便,所以又發展出來一個叫做繼承GenericServlet的方法來實現,后來又發現這個也不是很方便,所以又發展出來了繼承 HttpServevlet方式來實現
為了使學習更加深刻,我會把三種方式都實現下。
Servlet開發是很簡單的事情特別是用一些高級工具來開發的話,特別是像JBuilder來實現的話只要點一下就行了,但很遺憾的這樣的高級工具他會隱藏太多的細節,這樣的話就不利于我們的學習,特別是部署之類的他就給你全寫了,那么你就不好學到他servlet的底層運行機制和原理,為了讓大家理解的更為深刻,我還是先用JCreater來實現開發Servlet,后面當然會用到eclipse 或者JBuilder這些高級工具來開發。我們先過一點苦日子,然后再過一點好日子,這樣的話知識更加扎實一點了。
下面我們就來真正的來開發Servlet,首先用什么方法呢?用實現接口的方法來實現
在這里會引入servlet的生命周期這個特點。這第一個servlet非常簡單,就是寫一個Hello World 在瀏覽器中輸出。教程依照先簡單,再難,再綜合的順序進行。
下面是Servlet的開發流程。
1:首先在Tomcat的主目錄下的webapp下面建立一個WEB-INF文件夾
2:然后再WEB-INF文件夾下建立一個web.xml文件,記錄網站的配置信息
建立classes子文件夾 存放你的servlet
當然這個操作你可以自己完成 也可以在root目錄下拷貝一份
大家可以看到 這個地方傳遞過來了一個信息,什么信息呢?
對于Tomcat來講,你所有的這些網站,頁面對他來講都是web應用,他看來就是web應用,就是在webapp下面建立我們的網站。
比如建立一個文件夾叫做mywebsite,接下來文件夾中放什么東西呢,我們要放的就是WEB-INF文件夾,在里面放置web.xml文件以及classes文件夾和lib文件夾。
WEB-INF這個要注意大小寫,要注意大小寫要一模一樣才行的,名字都要一樣才行的。(Tomcat只認這個名字)
在這個文件夾下進行上述兩種操作,你可能會去問,為什么要這樣做呢?這個倒是沒有辦法的事情 因為這就是規范。
classes當然是存servlet用的 ,那個lib文件夾用來做什么呢?
用來存放這個應用匯用到的一些jar包,比如數據庫啦之類的,文件的下載要用的包都放在lib里面去,就是lib庫。
//這是我的第一個Servlet 使用實現Servlet接口的方式來開發 package com.tsinghua; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;public class Hello implements Servlet {// 該函數用于初始化該servlet, 類似于我們的類的構造函數// 該函數只是會被調用一次, 當用戶第一次訪問該servlet的時候被調用public void init(ServletConfig parm1) throws ServletException{System.out.println("init it !");}// 用于得到servlet配置文件 與生命周期無關public ServletConfig getServletConfig(){return null;}// service 函數用于處理業務邏輯// 程序員應當把業務邏輯代碼寫在這里// 該函數在用戶每次訪問servlet的時候都會被調用// ServletRequest 對象用于獲得客戶端信息,ServletResponse 對象用于向客戶端返回信息(客戶端可以理解為瀏覽器)// servelt jsp b/spublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException{System.out.println("service it");PrintWriter pw = res.getWriter();pw.println("hello world");}public String getServletInfo(){return " ";}// 銷毀servlet實例(釋放內存)// 1 reload 該servlet(webApp)// 2 關閉Tomcat 或者說 關機之后 都會調用這個函數public void destroy(){System.out.println("destory it");} }以上就是實現servlet接口的方式來開發servlet方式來代碼實現
其實以上方法都是回調函數 都是會在特定的時候特定的環境下自動調用的
其中init() 和 destroy() 都是只會調用一次的 但是 service 會在每一次都會被調用的
到現在還沒有完成 因為如果你想讓別人訪問到你的wervlet 的話 你就要部署你servlet
下面講授關于部署servlet的步驟(在web.xml 進行配置設置)
如果你要問為什么進行部署,那么還是一句話,規范。
<servlet><!--給你的servlet起名字,任意的--><servlet-name>hello_servlet</servlet-name><!--指明servlet的路徑,包名+類名 注意類名后不能加上java--><servlet-class>com.tsinghua.Hello</servlet-class> </servlet><servlet-mapping><!--mapping 自然就是映射了 于是乎 這個同上,一致--><servlet-name>hello_servlet</servlet-name><!--這是瀏覽器中輸入的訪問該servlet的url 任意的--><url-pattern>/sp</url-pattern> </servlet-mapping>用下面的控制臺命令編譯Hello.java之后,我們就可以啟動Tomcat來進行訪問了
啟動Tomcat的bin目錄下的startup.bat之后,
輸入
http://127.0.0.1:8080/guowuxin/sp
多次訪問之后就能體會到Servlet的生命周期
參考:
網絡:Servlet的本質是什么?為什么要有Servlet?_OceanStar的學習筆記的博客-CSDN博客_servlet的本質是什么
https://blog.csdn.net/weixin_42635759/article/details/100171907?
總結
以上是生活随笔為你收集整理的Servlet是什么的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 嵌入式操作系统一览
- 下一篇: 博弈论1(正则型博弈)