03 | 你應(yīng)該知道的Servlet規(guī)范和Servlet容器

瀏覽器發(fā)給服務(wù)端的是一個(gè)HTTP格式的請(qǐng)求,HTTP服務(wù)器收到這個(gè)請(qǐng)求后,需要調(diào)用服務(wù)端程序來(lái)處理,所謂的服務(wù)端程序就是你寫(xiě)的java類(lèi),一般來(lái)說(shuō)不同的請(qǐng)求需要不同的java類(lèi)來(lái)處理.

那么問(wèn)題來(lái)了,HTTP服務(wù)器怎么知道要帶哦用哪個(gè)java類(lèi)的哪個(gè)方法呢?最直接的作法就是在HTTP服務(wù)器里寫(xiě)一堆if else邏輯判斷:如果是A請(qǐng)求就調(diào)用X類(lèi)的M1方法,如果是B請(qǐng)求就調(diào)用Y類(lèi)的M2方法.但這樣做明顯由問(wèn)題,因?yàn)镠TTP服務(wù)器的代碼跟業(yè)務(wù)邏輯耦合在一起了,如果新加一個(gè)業(yè)務(wù)方法還要修改HTTP服務(wù)器的代碼.

那該怎么解決這個(gè)問(wèn)題呢?我們知道,面向接口編程是解決耦合的法寶,于是又一伙人就定義了一個(gè)接口,各種業(yè)務(wù)類(lèi)都必須實(shí)現(xiàn)這個(gè)接口,這個(gè)接口就叫Servlet接口,有時(shí)我們也把實(shí)現(xiàn)了Servlet接口的業(yè)務(wù)類(lèi)叫Servlet.

但是這里還有一個(gè)問(wèn)題,對(duì)于特定的請(qǐng)求,HTTP服務(wù)器如何知道由哪個(gè)Servlet來(lái)處理呢?Servlet又是由誰(shuí)來(lái)實(shí)例化呢?顯然HTTP服務(wù)器不適合這個(gè)工作,否則又和業(yè)務(wù)類(lèi)耦合了.

于是,還是那伙人又發(fā)明了Servlei容器,Servlet容器用來(lái)加載和管理業(yè)務(wù)類(lèi).HTTP服務(wù)器不直接跟業(yè)務(wù)類(lèi)打交道,而是把請(qǐng)求交給Servlet容器去處理,Servlet容器會(huì)將請(qǐng)求轉(zhuǎn)發(fā)到具體的Servlet,如果這個(gè)Servlet還沒(méi)創(chuàng)建,就加載并實(shí)例化這個(gè)Servlet,然后調(diào)用這個(gè)Servlet的接口方法,因此Servlet接口其實(shí)是Servlet容器跟業(yè)務(wù)類(lèi)之間的接口,下面我們通過(guò)一張圖來(lái)加深理解.


圖的左邊表示HTTP服務(wù)器直接調(diào)用具體的業(yè)務(wù)類(lèi),他們是緊耦合的.再看圖的右邊,HTTP服務(wù)器不直接調(diào)用業(yè)務(wù)類(lèi),而是把請(qǐng)求交給容器處理,容器通過(guò)Servlet接口調(diào)用業(yè)務(wù)類(lèi).因此Servlet接口和Servlet容器的出現(xiàn),達(dá)到了HTTP服務(wù)器與業(yè)務(wù)類(lèi)解耦的目的.

而Servlet接口和Servlet容器這一整套規(guī)范叫做Servlet規(guī)范.Tomcat和Jetty都按照Servlet規(guī)范的要求實(shí)現(xiàn)了Servlet容器,同時(shí)他們也具有HTTP服務(wù)器的功能,作為Java程序員,如果我們要實(shí)現(xiàn)新的業(yè)務(wù)功能,只需要實(shí)現(xiàn)一個(gè)Servlet,并把它注冊(cè)到Tomcat(Servlet容器) 中,剩下的事情就由Tomcat幫我們處理了.

接下來(lái)我們看看Servlet接口具體是怎么定義的,以及Servlet規(guī)范又有哪些要重點(diǎn)關(guān)注的地方呢?

Servlet接口

Servlet 接口定義了下面五個(gè)方法:

public interface Servlet {

? ? void init(ServletConfig config) throws ServletException;


? ? ServletConfig getServletConfig();


? ? void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;


? ? String getServletInfo();


? ? void destroy();

}

其中最重要的是service方法,具體業(yè)務(wù)類(lèi)在這個(gè)方法里實(shí)現(xiàn)處理邏輯.這個(gè)方法有兩個(gè)參數(shù):ServletRequest和ServletResponse,ServletRequest用來(lái)封裝請(qǐng)求信息,ServletResponse用來(lái)封裝響應(yīng)信息,因此本質(zhì)上這兩個(gè)類(lèi)是對(duì)通信協(xié)議的封裝.

比如HTTP協(xié)議中的請(qǐng)求和響應(yīng)就是對(duì)應(yīng)了HttpServletRequest和HttpServletResponse這兩個(gè)類(lèi).你可以通過(guò)HttpServletRequest來(lái)獲取所有請(qǐng)求相關(guān)的信息,包括請(qǐng)求路徑,Cookie,HTTP頭,請(qǐng)求參數(shù)等,此外,我們還可以通過(guò)HttpServletRequest來(lái)創(chuàng)建和獲取Session,而HttpServletResponse是用來(lái)封裝HTTP響應(yīng)的.

你可以看到接口中還有兩個(gè)跟聲明周期有關(guān)的方法init和destory,這是一個(gè)比較貼心的設(shè)計(jì),Servlet容器在加載Servlet類(lèi)的時(shí)候會(huì)調(diào)用init方法,在卸載的時(shí)候會(huì)調(diào)用destory方法,我們可能會(huì)在init方法里初始化一些資源,并在destory方法里釋放這些資源,比如Spring MVC中的DispatcherServlet,就是在init方法里創(chuàng)建了自己的Spring容器.

你還會(huì)注意到ServletConfig這個(gè)類(lèi),ServletConfig的作用就是封裝Servlet的初始化參數(shù),你可以在web.xml給Servlet參數(shù),并在程序里通過(guò)getServletConfig方法拿到這些參數(shù),

我們知道,有接口一般就有抽象類(lèi),抽象類(lèi)用來(lái)實(shí)現(xiàn)接口和封裝通用的邏輯,因此Servlet規(guī)范提供了GenericServlet抽象類(lèi),我們可以通過(guò)擴(kuò)展它來(lái)實(shí)現(xiàn)Servlet.雖然Servlet規(guī)范并不在乎通信協(xié)議是什么,但是大多數(shù)的Servlet都是在HTTP環(huán)境中處理的,因此Servlet規(guī)范還提供了HttpServlet來(lái)繼承GenericServlet,并且加入了HTTP特性,這樣我們通過(guò)繼承HttpServlet類(lèi)來(lái)實(shí)現(xiàn)自己的Servlet,只需要重寫(xiě)兩個(gè)方法:doGet和doPost.

Servlet容器

前面提到,為了解耦,HTTP服務(wù)器不直接調(diào)用Servlet,而是把請(qǐng)求交給Servlet容器來(lái)處理,那Servlet容器又是怎么工作的呢?接下來(lái)我會(huì)介紹Servlet容器大體的工作流程,一起來(lái)聊聊我們關(guān)心的兩個(gè)話(huà)題:Web應(yīng)用的目錄格式是什么樣的,以及我該怎樣擴(kuò)展和定制化Servlet容器的功能.

工作流程

當(dāng)客戶(hù)請(qǐng)求某個(gè)資源時(shí),HTTP服務(wù)器會(huì)用一個(gè)ServletRequest對(duì)象把客戶(hù)的請(qǐng)求信息封裝起來(lái),然后調(diào)用Servlet容器的service方法,Servlet容器拿到請(qǐng)求后,根據(jù)請(qǐng)求的URL和Servlet的映射關(guān)系,找到相應(yīng)的Servlet,如果Servlet還沒(méi)有被加載,就用反射機(jī)制創(chuàng)建這個(gè)Servlet,并調(diào)用Servlet的init方法來(lái)完成初始化,接著調(diào)用Servlet的service方法來(lái)處理請(qǐng)求,把ServletResponse對(duì)象返回給HTTP服務(wù)器,HTTP服務(wù)器會(huì)把響應(yīng)發(fā)送給客戶(hù)端,同樣我通過(guò)一張圖來(lái)幫助你理解;

Web應(yīng)用

Servlet容器會(huì)實(shí)例化和調(diào)用Servlet,那Servlet是怎么注冊(cè)到Servlet容器中的呢?一般來(lái)說(shuō),我們是以Web應(yīng)用程序的方式來(lái)部署Servlet的,而根據(jù)Servlet規(guī)范,Web應(yīng)用程序有一定的目錄結(jié)構(gòu),在這個(gè)目錄下分別放置了Servlet的類(lèi)文件,配置文件以及靜態(tài)資源,Servlet容器通過(guò)讀取配置文件,就等找到并加載Servlet.Web應(yīng)用的目錄結(jié)構(gòu)大概是下面這樣:

| - MyWebApp

? ? ? | -? WEB-INF/web.xml? ? ? ? -- 配置文件,用來(lái)配置 Servlet 等

? ? ? | -? WEB-INF/lib/? ? ? ? ? -- 存放 Web 應(yīng)用所需各種 JAR 包

? ? ? | -? WEB-INF/classes/? ? ? -- 存放你的應(yīng)用類(lèi)邀窃,比如 Servlet 類(lèi)

? ? ? | -? META-INF/? ? ? ? ? ? ? -- 目錄存放工程的一些信息

Servlet規(guī)范里定義了ServletContext這個(gè)接口來(lái)對(duì)應(yīng)一個(gè)Web應(yīng)用,Web應(yīng)用部署好后,Servlet容器在啟動(dòng)時(shí)會(huì)加載Web應(yīng)用,并為每個(gè)Web應(yīng)用創(chuàng)建唯一的ServletContext對(duì)象.你可以把ServletContext堪稱(chēng)時(shí)一個(gè)全局對(duì)象,一個(gè)Web應(yīng)用可能有多個(gè)Servlet,這些Servlet可以通過(guò)全局的ServletContext來(lái)共享數(shù)據(jù),這些數(shù)據(jù)包括Web應(yīng)用的初始化參數(shù),Web應(yīng)用目錄下的文件資源等,由于Servlet Context持有所有Servlet實(shí)例,你還可以通過(guò)它來(lái)實(shí)現(xiàn)Servlet請(qǐng)求的轉(zhuǎn)發(fā).

擴(kuò)展機(jī)制

不知道你有沒(méi)有發(fā)現(xiàn),引入了Servlet規(guī)范后,你不需要關(guān)心Socket網(wǎng)絡(luò)通信,不需要關(guān)心HTTP協(xié)議,也不需要關(guān)心你的業(yè)務(wù)類(lèi)時(shí)如何被實(shí)例化和調(diào)用的.因?yàn)檫@些都被Servlet規(guī)范標(biāo)準(zhǔn)化了,你只關(guān)心怎么實(shí)現(xiàn)你的業(yè)務(wù)邏輯,這對(duì)程序員來(lái)說(shuō)是件好事,但也有不方便的一面,所謂規(guī)范就是說(shuō)大家都要遵守,就會(huì)千篇一律,但是如果這個(gè)規(guī)范不能滿(mǎn)足你的業(yè)務(wù)的個(gè)性化需求,就有問(wèn)題了,因此設(shè)計(jì)一個(gè)規(guī)范或者一個(gè)中間件,要充分考慮可擴(kuò)展性,Servlet規(guī)范提供了兩種擴(kuò)展機(jī)制:Filter和Listener.

Filter是過(guò)濾器,這個(gè)接口允許你對(duì)請(qǐng)求和響應(yīng)做一些統(tǒng)一的定制化處理,比如你可以根據(jù)請(qǐng)求的頻率來(lái)限制訪(fǎng)問(wèn),或者根據(jù)國(guó)家地區(qū)的不同來(lái)修改響應(yīng)內(nèi)容,過(guò)濾器的工作原理是這樣的:Web應(yīng)用部署完成后,Servlet容器需要實(shí)例化Filter并把Filter鏈接成一個(gè)FilterChain,當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),獲取第一個(gè)Filter調(diào)用doFilter方法,doFilter方法負(fù)責(zé)調(diào)用這個(gè)FilterChain中的下一個(gè)Filter.

Listener是監(jiān)聽(tīng)器,這是另一種擴(kuò)展機(jī)制 ,當(dāng)web應(yīng)用在Servlet容器中運(yùn)行時(shí),Servlet容器內(nèi)部會(huì)不斷的發(fā)生各種事件,如web應(yīng)用的啟動(dòng)和停止,用戶(hù)請(qǐng)求到達(dá)等,Servlet容器提供了一些默認(rèn)的監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)這些事件,當(dāng)事件發(fā)生時(shí),Servlet容器會(huì)負(fù)責(zé)調(diào)用監(jiān)聽(tīng)器的方法.當(dāng)然,你可以定義自己的監(jiān)聽(tīng)器去監(jiān)聽(tīng)你感興趣的事件,將監(jiān)聽(tīng)器配置在web.xml中,比如Spring就實(shí)現(xiàn)了自己的監(jiān)聽(tīng)器,來(lái)監(jiān)聽(tīng)ServletContext的啟動(dòng)事件,目的是當(dāng)Servlet容器啟動(dòng)時(shí),創(chuàng)建并初始化全局的Spring容器.

本期精華

今天我們學(xué)習(xí)了什么是Servlet,回顧一下,Servlet本質(zhì)上是一個(gè)接口,實(shí)現(xiàn)了Servlet接口的業(yè)務(wù)類(lèi)也叫Servlet.Servlet接口其實(shí)是Servlet容器跟具體Servlet業(yè)務(wù)類(lèi)之間的接口,Servlet接口跟Servlet容器這整套規(guī)范叫做Servlet規(guī)范,而Servlet規(guī)范使得程序員可以專(zhuān)注業(yè)務(wù)邏輯的開(kāi)發(fā),同時(shí)Servlet規(guī)范也給開(kāi)發(fā)者提供了擴(kuò)展的機(jī)制Filter和Listener.

Filter是干預(yù)過(guò)程的,它是過(guò)程的一部分,是基于過(guò)程行為的.

Listener是基于狀態(tài)的,任何行為改變同一個(gè)狀態(tài),觸發(fā)的事件是一致的.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末主儡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子藏古,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裹芝,死亡現(xiàn)場(chǎng)離奇詭異恨搓,居然都是意外死亡院促,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)斧抱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)常拓,“玉大人,你說(shuō)我怎么就攤上這事辉浦∨В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵宪郊,是天一觀的道長(zhǎng)掂恕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)弛槐,這世上最難降的妖魔是什么懊亡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮丐黄,結(jié)果婚禮上斋配,老公的妹妹穿的比我還像新娘。我一直安慰自己灌闺,他們只是感情好艰争,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著桂对,像睡著了一般甩卓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕉斜,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天逾柿,我揣著相機(jī)與錄音缀棍,去河邊找鬼。 笑死机错,一個(gè)胖子當(dāng)著我的面吹牛爬范,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弱匪,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼青瀑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了萧诫?” 一聲冷哼從身側(cè)響起斥难,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帘饶,沒(méi)想到半個(gè)月后哑诊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡及刻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年镀裤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片提茁。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淹禾,死狀恐怖馁菜,靈堂內(nèi)的尸體忽然破棺而出茴扁,到底是詐尸還是另有隱情,我是刑警寧澤汪疮,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布峭火,位于F島的核電站,受9級(jí)特大地震影響智嚷,放射性物質(zhì)發(fā)生泄漏卖丸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一盏道、第九天 我趴在偏房一處隱蔽的房頂上張望稍浆。 院中可真熱鬧,春花似錦猜嘱、人聲如沸衅枫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)弦撩。三九已至,卻和暖如春论皆,著一層夾襖步出監(jiān)牢的瞬間益楼,已是汗流浹背猾漫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留感凤,地道東北人悯周。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像陪竿,于是被迫代替她去往敵國(guó)和親队橙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Based on Java? Servlet Specification v3.1 [TOC] Servlet和S...
    0x70e8閱讀 1,319評(píng)論 0 7
  • Servlet接口 Servlet規(guī)范的核心接口即是Servlet接口萨惑,它是所有Servlet類(lèi)必須實(shí)現(xiàn)的接口捐康,在...
    java日記閱讀 1,824評(píng)論 0 2
  • IOC 控制反轉(zhuǎn)容器控制程序?qū)ο笾g的關(guān)系,而不是傳統(tǒng)實(shí)現(xiàn)中庸蔼,有程序代碼之間控制解总,又名依賴(lài)注入。All 類(lèi)的創(chuàng)建姐仅,...
    irckwk1閱讀 946評(píng)論 0 0
  • 這部分主要是與Java Web和Web Service相關(guān)的面試題花枫。 96、闡述Servlet和CGI的區(qū)別? 答...
    雜貨鋪老板閱讀 1,408評(píng)論 0 10
  • 轉(zhuǎn)自陳明乾的博客掏膏,可能有一定更新劳翰。 轉(zhuǎn)原文聲明:原創(chuàng)作品,允許轉(zhuǎn)載馒疹,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章 原始出處 佳簸、...
    C86guli閱讀 4,695評(píng)論 6 72