章節(jié)目錄
- 什么是Servlet
- Servlet 烘嘱、ServletContext赚导、Servlet Container被因、web 容器之間的區(qū)別
- Servlet卿拴、ServletConfig、GenericServlet梨与、HttpServlet堕花、自定義Servlet 之間的聯(lián)系
- HttpServlet 源碼解析
- Servlet 是否是線程安全的-線程非安全
1.什么是Servlet
1.1 定義
首先看Servlet接口源碼中對(duì)于Servlet的定義
A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the
HyperText Transfer Protocol.
譯文如下:
Servlet 是運(yùn)行在Web服務(wù)器上的小型Java程序,Servlet 接受并響應(yīng)來(lái)自Web 客戶端的請(qǐng)求粥鞋,通常請(qǐng)求通過(guò)HTTP協(xié)議進(jìn)行請(qǐng)求傳輸缘挽。
總結(jié)如下:
1.Java Servlet 運(yùn)行在 Web 服務(wù)器(Web 容器)之上,即1個(gè)Web服務(wù)器上可部署運(yùn)行多個(gè)Servlet呻粹。
2.Servlet 的功能是-接收來(lái)自客戶端發(fā)送的 HTTP 請(qǐng)求壕曼,并在接收 HTTP 請(qǐng)求
之后做相關(guān)處理,處理完成之后將數(shù)據(jù)響應(yīng)給客戶端等浊。
2. Servlet 腮郊、ServletContext、Servlet Container凿掂、web 容器之間的區(qū)別
2.1 servlet
servlet 用來(lái)接收伴榔、處理請(qǐng)求纹蝴,并返回?cái)?shù)據(jù)。
2.2 ServletContext
ServletContext (Servlet上下文對(duì)象)是web應(yīng)用服務(wù)器啟動(dòng)之后創(chuàng)建的對(duì)象踪少,且只在web應(yīng)用服務(wù)器啟動(dòng)時(shí)創(chuàng)建一次塘安,所有的Servlet 可以共享并修改在ServletContext設(shè)置的共享變量。
源碼注釋如下所示
Defines a set of methods that a servlet uses to communicate with its servlet
container, for example, to get the MIME type of a file, dispatch requests, or
write to a log file.
譯文如下:
ServletContext 中定義了讓單獨(dú)的Servlet與Servlet容器進(jìn)行交互的一系列操作方法援奢,如獲取文件的 MIME type兼犯,如
text/htm
、image/gif
集漾,getRequestDispatcher操作(重定向操作)301
-永久重定向
對(duì)應(yīng)forward()
切黔、302
-臨時(shí)重定向
對(duì)應(yīng)redirect()
、或者寫(xiě)日志文件操作具篇。
2.3.Servlet Contanier
Servlet 容器纬霞,可以在其之上搭載多個(gè)Servlet,類(lèi)似于Servlet是子彈驱显、Servlet 容器是槍诗芜。
2.4.web 容器
web 容器 即 web服務(wù)器,Servlet 容器搭載于Web 容器之上埃疫,請(qǐng)求過(guò)來(lái)之后伏恐,
先到達(dá)至web服務(wù)器,web服務(wù)器將請(qǐng)求傳遞至Servlet容器栓霜,Servlet容器根據(jù)
一定的匹配規(guī)則翠桦,如Servlet-Mapping 將 請(qǐng)求傳遞至對(duì)應(yīng)的Servlet處理程序,
這便是請(qǐng)求的部分傳遞過(guò)程胳蛮。即request->web container->servlet container->servlet
销凑。
3.Servlet、ServletConfig仅炊、GenericServlet闻鉴、HttpServlet、自定義Servlet 之間的聯(lián)系
3.1 相關(guān)類(lèi)圖
接下來(lái)茂洒,各個(gè)擊破:
3.2 Servlet 接口
如下圖所示Servlet 接口中聲明的public 方法:
Servlet 中定義了5個(gè)方法:
init(ServletConfig)
:Servlet在容器啟動(dòng)時(shí)不進(jìn)行實(shí)例化所以不調(diào)用init()方法,
只有當(dāng)?shù)谝粋€(gè)請(qǐng)求通過(guò)Servlet 中 Service()方法進(jìn)行處理時(shí)才進(jìn)行實(shí)例化瓶竭,且只實(shí)例化一次督勺,該方法被Servlet Container 調(diào)用。
getServletConfig()
:獲取與當(dāng)前Servlet 綁定的 ServletConfig 對(duì)象斤贰,
ServletConfig 對(duì)象包含 web.xml 配置文件中配置的 ServletName智哀、
initParameter等信息。通過(guò)ServletConfig 對(duì)象可以獲取到ServletContext對(duì)
象荧恍,所以ServletConfig 對(duì)象是 不同 Servlet 之間共享Servlet 容器信息 的橋
梁瓷叫。
service()
:接受客戶端請(qǐng)求對(duì)象屯吊、執(zhí)行業(yè)務(wù)操作、利用響應(yīng)對(duì)象響應(yīng)客戶端請(qǐng)
求摹菠。
getServletInfo()
:返回Servlet相關(guān)信息盒卸,如Servlet 名稱(chēng)、作者次氨、版本等信
息蔽介。
destroy()
:當(dāng)容器監(jiān)測(cè)到一個(gè)Servlet 服務(wù)從容器中被移除,容器調(diào)用該方
法煮寡、釋放資源虹蓄、該方法只能被調(diào)用一次。
3.3 Servlet 生命周期
-
初始化
階段調(diào)用 init() 方法 -
響應(yīng)客戶端請(qǐng)求
階段調(diào)用 service() 方法 -
終止
階段調(diào)用destroy方法
3.4 Servlet幸撕、ServletConfig薇组、GenericServlet、HttpServlet
- 頂層接口Servlet坐儿、ServletConfig律胀、
- GenericServlet 實(shí)現(xiàn) implements 上述兩個(gè)頂層接口、
- ServletConfig 中聲明 getServletContext方法挑童、
- Servlet 中聲明 getServletConfig 方法累铅、
- HttpServlet extend GenericServlet;
所以在Servlet 中獲取servletContext對(duì)象的方法有以下兩種方式:
方式一
ServletContext servletContext = getServletContext();
方式二
ServletContext servletContext = getServletConfig().getServletContext()l;
4.HttpServlet 源碼解析
4.1 HttpServlet實(shí)例域
HttpServlet 是繼承自 GenericServlet 的抽象類(lèi)
實(shí)例域定義如下圖所示:
- GET : 獲取由請(qǐng)求 URL 標(biāo)識(shí)的資源
- POST : 向 Web 服務(wù)器發(fā)送無(wú)限制長(zhǎng)度的數(shù)據(jù)
- PUT : 存儲(chǔ)一個(gè)資源到請(qǐng)求的 URL
- DELETE : 刪除由 URL 標(biāo)識(shí)的資源
- HEAD : 返回 URL 標(biāo)識(shí)的頭信息
- OPTIONS : 返回服務(wù)器支持的 HTTP 方法
- TRACE : 返回 TRACE 請(qǐng)求附帶的頭字段
4.2 對(duì)應(yīng)的服務(wù)方法
- doGet():調(diào)用服務(wù)器的資源站叼,并將其作為響應(yīng)返回給客戶端娃兽。doGet() 操作會(huì)將url 中顯式傳遞的參數(shù) 傳遞給 Servlet,Servlet 通過(guò) getRequestParam(paramName) 獲取相關(guān)參數(shù)尽楔,這在系統(tǒng)的安全方面可能帶來(lái)一些問(wèn)題, 比如說(shuō), 用戶登錄時(shí), 表單里的用戶名和密碼需要發(fā)送到服務(wù)器端, doGet() 調(diào)用會(huì)在瀏覽器的 URL 里顯示用戶名和密碼.
- doPost() : 它用于把客戶端的數(shù)據(jù)傳給服務(wù)端, 使用它可以以隱藏方式給服務(wù)器端發(fā)送數(shù)據(jù). Post 適合發(fā)送大量數(shù)據(jù).
- doPut() : 調(diào)用和 doPost() 相似, 并且它允許客戶端把真正的文件存放在服務(wù)器上, 而不僅僅是傳送數(shù)據(jù).
- doDelete() : 它允許客戶端刪除服務(wù)器端的文件或者 Web 頁(yè)面.它的使用非常少.
- doHead() : 它用于處理客戶端的 Head 調(diào)用,并且返回一個(gè) response. 當(dāng)客戶端只需要響應(yīng)的 Header 時(shí),它就發(fā)出一個(gè)Header 請(qǐng)求.這種情況下客戶端往往關(guān)心響應(yīng)的長(zhǎng)度和響應(yīng)的 MIME 類(lèi)型.
- doOptions(): 它用于處理客戶端的 Options 調(diào)用,通過(guò)這個(gè)調(diào)用, 客戶端可以獲得此 Servlet 支持的方法.如果 Servlet 覆蓋了 doPost() 方法, 那么將返回: Allow: POST, TRACE, OPTIONS, HEAD
- doTrace:處理 TRACE 請(qǐng)求
4.3 HttpServlet 實(shí)現(xiàn) Servlet 中的Servlet方法
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
//HttpServlet 重載 的Service() 方法投储,使用到了編譯時(shí)多態(tài),因?yàn)閰?shù)類(lèi)型不一樣阔馋。
service(request, response);
}
}
4.4 HttpServlet 重載的 Service() 方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 從 HTTP 請(qǐng)求中取得這次請(qǐng)求所使用的 HTTT 方法
String method = req.getMethod();
// 如果這次請(qǐng)求使用 GET 方法
if (method.equals(METHOD_GET)) {
// 取得這個(gè) Servlet 的最后修改的時(shí)間
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
//-1 代表這個(gè) Servlet 不支持最后修改操作玛荞,直接調(diào)用 doGet() 進(jìn)行處理 HTTP GET 請(qǐng)求
doGet(req, resp);
} else {
long ifModifiedSince;
try {
// 如果這個(gè) Servlet 支持最后修改操作,取得請(qǐng)求頭中包含的請(qǐng)求的最后修改時(shí)間
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
// 如果請(qǐng)求頭中包含的修改時(shí)間早于這個(gè) Servlet 的最后修改時(shí)間呕寝,說(shuō)明這個(gè) Servlet 自從客戶上一次 HTTP 請(qǐng)求已經(jīng)被修改了 , 設(shè)置最新修改時(shí)間到響應(yīng)頭中
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
5.Servlet 是否是線程安全的
Servlet 是線程非安全的勋眯。這是單例模式的通病。
因?yàn)閟ervlet對(duì)象是單例模式的創(chuàng)建型對(duì)象下梢,在一次Servlet Container 啟停過(guò)程中客蹋,整個(gè) Servlet 生命周期內(nèi)只維持一個(gè)Servlet對(duì)象,而對(duì)于每一個(gè)請(qǐng)求應(yīng)用服務(wù)器都會(huì)從線程池中excute 一個(gè)線程去執(zhí)行一個(gè)runnertask(request請(qǐng)求)
所以如果在一個(gè)Servlet中定義了實(shí)例域孽江,那么就會(huì)產(chǎn)生多線程并發(fā)修改共享變量的線程安全問(wèn)題讶坯,解決這個(gè)問(wèn)題的辦法是對(duì)多線程對(duì)共享變量的操作采用同步
操作,或者采用ThreadLocal
將每個(gè)線程與其自身的變量綁定在一起岗屏。