溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解Servlet及Servlet容器的概念以及使用注意點(diǎn)

發(fā)布時(shí)間:2021-11-10 09:44:52 來(lái)源:億速云 閱讀:228 作者:柒染 欄目:大數(shù)據(jù)

如何理解Servlet及Servlet容器的概念以及使用注意點(diǎn),很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

Servlet

Servlet是一個(gè)服務(wù)器端編程的組件,主要能力產(chǎn)生動(dòng)態(tài)的數(shù)據(jù)內(nèi)容,和任何java組件一樣,Servlet也是平臺(tái)不相關(guān)的。 Servlet的運(yùn)行依賴于一個(gè)容器 (或者叫servlet engine),容器在運(yùn)行時(shí)會(huì)把Servlet字節(jié)碼加載到虛擬機(jī)中生存一個(gè)servlet實(shí)例,并負(fù)責(zé)管理servlet實(shí)例的生命周期。

和servlet交互的對(duì)面可以是一個(gè)web瀏覽器,也可以是任何一個(gè)支持http協(xié)議的客戶端程序,客戶端和Servlet之間交互遵循request/response模式進(jìn)行,實(shí)際上Servlet設(shè)計(jì)為并不僅僅局限于http協(xié)議,任何基于request/response的協(xié)議Servlet都支持。

Servlet容器

Servlet容器為servlet提供運(yùn)行環(huán)境,servlet容器通常實(shí)現(xiàn)為一個(gè)Web服務(wù)器或者應(yīng)用服務(wù)器的一部分。

Servlet容器的核心功能是接受請(qǐng)求、解碼請(qǐng)求內(nèi)容,然后把解析后請(qǐng)求內(nèi)容分發(fā)到相關(guān)的某個(gè)Servlet,Servlet處理后,容器會(huì)把Servlet返回的數(shù)據(jù)內(nèi)容進(jìn)行編碼并發(fā)送給客戶端。這個(gè)過(guò)程中容器可以在把請(qǐng)求分發(fā)給servlet之前修改請(qǐng)求內(nèi)容,也可以在把servlet的返回內(nèi)容發(fā)送到客戶端之前修改響應(yīng)內(nèi)容。

Servlet容器必須要提供http 1.0/1.1的支持,也可以支持https。

Servlet有完整的生命周期

一個(gè)servlet的生命周期階段主要包括:加載并創(chuàng)建servlet實(shí)例、初始化servlet實(shí)例、處理請(qǐng)求以及銷毀。

servlet的生命周期階段完全由servlet容器來(lái)管理,從編程的角度看生命周期主要體現(xiàn)在Servlet接口中定義的三個(gè)方法:

  • init方法,讓?xiě)?yīng)用程序有機(jī)會(huì)執(zhí)行初始化

  • service方法,應(yīng)用程序處理請(qǐng)求并響應(yīng)內(nèi)容的地方

  • destroy方法,讓?xiě)?yīng)用程序有機(jī)會(huì)釋放servlet持有的各種資源

任何一個(gè)servlet都必須直接或者間接實(shí)現(xiàn)這三個(gè)方法。 Servlet實(shí)例則由容器負(fù)責(zé)實(shí)例化,不需要應(yīng)用程序參與,容器可以在啟動(dòng)完成后就開(kāi)始加載servlet,也可以在第一次servlet被選中處理請(qǐng)求時(shí)才加載此servlet。

servlet容器完成servlet的實(shí)例化后就開(kāi)始進(jìn)行初始化,容器會(huì)調(diào)用 init 方法并傳遞一個(gè) ServletConfig 對(duì)象的引用,通過(guò)ServletConfig引用可以在Servlet中感知到Servlet的一些詳細(xì)配置參數(shù)和代表應(yīng)用程序的ServletContext引用。一個(gè)servlet實(shí)例整個(gè)生命周期過(guò)程中init方法只會(huì)被容器調(diào)用一次。

只有初始化完成的servlet才能進(jìn)一步提供請(qǐng)求服務(wù),如果init方法中拋出ServletException異?;蛘邲](méi)有在容器定義的時(shí)間內(nèi)返回,則servlet容器認(rèn)為初始化失敗,此servlet不能提供服務(wù)。init方法拋出的可能是一個(gè)UnavailableException異常,這個(gè)異常暗示容器此servlet目前不可用:永久性不可用或者臨時(shí)不可用,如果是臨時(shí)性不可用則UnavailableException應(yīng)該帶上一個(gè)不可用預(yù)估時(shí)間秒數(shù),這種情況下容器不會(huì)直接失敗,而是會(huì)阻塞預(yù)估的時(shí)長(zhǎng)然后重新創(chuàng)建一個(gè)servlet實(shí)例并再次執(zhí)行初始化。

servlet成功初始化后就可以處理請(qǐng)求了,容器會(huì)調(diào)用service方法,并傳遞ServletRequest和ServletResponse兩個(gè)引用,ServletRequest封裝了客戶端請(qǐng)求信息,用戶應(yīng)用程序的則負(fù)責(zé)填充ServletResponse對(duì)象,service方法返回后流程被容器接管。

service方法如果拋出ServletException異常代表失敗,容器需要作出合適的下一步處理,sevlet規(guī)范定義如下過(guò)程:service方法如果拋出實(shí)際是一個(gè)UnavailableException異常則表示此servlet目前不可用,同樣有永久性不可用和臨時(shí)不可用兩種情況,如果是永久性不可用,則容器會(huì)調(diào)用servlet的destroy方法清理然后釋放此servlet,并給客戶端響應(yīng)SC_NOT_FOUND(404)狀態(tài);如果是臨時(shí)性不可用,則servlet容器在預(yù)估的不可用時(shí)間內(nèi)會(huì)直接拒絕所有的匹配到此servlet的請(qǐng)求而直接響應(yīng)SC_SERVICE_UNAVAILABLE(503)狀態(tài),并響應(yīng)一個(gè) Retry-After header,容器也可以選擇忽略這兩種不可用的差別,直接當(dāng)做是永久性不可用并清理釋放servlet對(duì)象。

一個(gè)servlet實(shí)例正常情況下應(yīng)該是永久駐留在容器中,直到關(guān)閉容器時(shí)才會(huì)銷毀容器中的servlet實(shí)例。在清理一個(gè)servlet實(shí)例之前,容器總是會(huì)調(diào)用destroy方法,用戶通??梢栽赿estroy方法中做一些資源清理、持久化等工作,一旦一個(gè)servlet的destroy方法被調(diào)用后,容器就會(huì)完全釋放它以便垃圾收集器回收。

用Servlet編程

通常我們要在應(yīng)用程序中實(shí)現(xiàn)一個(gè)Servlet類的話,不需要直接實(shí)現(xiàn)Servlet接口,而是通過(guò)擴(kuò)展GenericServlet,在http環(huán)境中的話就就可以擴(kuò)展HttpServlet,GenericServlet對(duì)Servlet接口做了基本實(shí)現(xiàn),只是預(yù)留了service方法需要用戶實(shí)現(xiàn), GenericServlet的實(shí)現(xiàn)沒(méi)有綁定到任何具體的應(yīng)用層協(xié)議,它是通用的。

在HTTP協(xié)議環(huán)境中,平臺(tái)還提供了一個(gè)HttpServlet的實(shí)現(xiàn)類,HttpServlet就擴(kuò)展自GenericServlet,HttpServlet完成實(shí)現(xiàn)了service方法,service方法中的實(shí)現(xiàn)方式是根據(jù)不同的http動(dòng)詞作了一些基本情況的判斷處理,然后把正常情況下的過(guò)程委托到幾個(gè)特定的http方法:doGet,doPost,doPut,...,絕大多數(shù)情況我們自己的Servlet實(shí)現(xiàn)類應(yīng)該選擇擴(kuò)展HttpServlet,并且不應(yīng)該直接重寫(xiě)service方法,而是應(yīng)該重寫(xiě)doXXX方法。

public class RequestParameterServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
        // …
    }
}

應(yīng)該重寫(xiě)無(wú)參數(shù)init方法

Servlet接口中定義的init方法帶有一個(gè)ServletConfig參數(shù):

public void init(ServletConfig config) throws ServletException;

init方法在GenericServlet中做了一個(gè)基本實(shí)現(xiàn):

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

在GenericServlet版本的init實(shí)現(xiàn)中,先把容器傳遞過(guò)來(lái)的ServletConfig引用保存下來(lái),然后調(diào)用了一個(gè)無(wú)參數(shù)的init方法,這個(gè)無(wú)參數(shù)的int方法正是預(yù)留給用戶實(shí)現(xiàn)的,因此,不管我們的Servlet類是擴(kuò)展了GenericServlet,還是擴(kuò)展了HttpServlet,如果有初始化的需求,那么我們應(yīng)該重寫(xiě)GenericServlet中的無(wú)參數(shù)init方法。

多線程環(huán)境中的Servlet

通常情況下,容器只會(huì)為web descriptor中聲明的每個(gè)servlet創(chuàng)建一個(gè)實(shí)例,這意味著在多線程情況下,其service方法是并發(fā)狀態(tài)下運(yùn)行,因此我們?cè)趯?shí)現(xiàn)service方法時(shí)要自己保證其線程安全性,通常的做法是不在Servlet實(shí)現(xiàn)類中使用共享字段,或者對(duì)service方法中的相關(guān)代碼段加鎖。

另一種方式是我們的Servlet類實(shí)現(xiàn)SingleThreadModel接口,SingleThreadModel是一個(gè)標(biāo)識(shí)接口,servlet容器保證實(shí)現(xiàn)SingleThreadModel的servlet實(shí)例的service方法在同一時(shí)間只有一個(gè)線程訪問(wèn),容器的做法可能是在servlet實(shí)例上加鎖,容器也可能會(huì)為同一個(gè)Servlet類創(chuàng)建出多個(gè)實(shí)例并在容器中緩存它們,然后對(duì)每一個(gè)請(qǐng)求分發(fā)到其中一個(gè)空閑的servlet實(shí)例。但是并不推薦使用SingleThreadModel接口,因?yàn)镾ingleThreadModel方式并不能完全解決線程安全問(wèn)題,例如session中的數(shù)據(jù)并不能保證線程安全,并且在servlet 2.4版本中已經(jīng)廢棄。

如何處理請(qǐng)求的字符編碼

客戶端的所有請(qǐng)求數(shù)據(jù),包括請(qǐng)求路徑,協(xié)議,請(qǐng)求頭以及請(qǐng)求內(nèi)容等這些數(shù)據(jù)全部都被容器封裝在一個(gè)ServletRequest對(duì)象中,從ServletRequest對(duì)象中讀取請(qǐng)求內(nèi)容時(shí)必須要知道數(shù)據(jù)是如何編碼的,否則就不可能正確解析請(qǐng)求數(shù)據(jù)。

由于目前大多數(shù)瀏覽器客戶端在發(fā)送請(qǐng)求時(shí)都沒(méi)有在Content-Type域中提供字符編碼的標(biāo)記,因此servlet規(guī)范中明確要求所有servlet容器在讀取請(qǐng)求數(shù)據(jù)時(shí)應(yīng)該默認(rèn)使用“ISO-8859-1”來(lái)解析數(shù)據(jù)。如果客戶端沒(méi)有發(fā)送請(qǐng)求數(shù)據(jù)的編碼格式,規(guī)范定義調(diào)用getCharacterEncoding方法應(yīng)該返回null。

如果客戶端沒(méi)有發(fā)送請(qǐng)求內(nèi)容的編碼格式,并且如果容器設(shè)定的默認(rèn)編碼和實(shí)際的請(qǐng)求內(nèi)容編碼又不一致時(shí),就會(huì)解析得到錯(cuò)誤的數(shù)據(jù),為了糾正這種情況ServletRequest另外供了一個(gè)setCharacterEncoding方法,編程人員可以調(diào)用此方法覆蓋容器提供的編碼格式來(lái)讀取數(shù)據(jù),此方法的調(diào)用一定要在讀取任何請(qǐng)求內(nèi)容之前,否則不會(huì)有效果。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI