图解 深入浅出 JavaWeb:Servlet 再说几句
Writer????? :BYSocket(泥沙磚瓦漿木匠)
微???????? 博:BYSocket
豆???????? 瓣:BYSocket
FaceBook:BYSocket
Twitter??? :BYSocket
上一篇的《?Servlet必會(huì)必知?》受到大家一致好評(píng) — (感謝?讀者 及 OSC 推薦 每日一’搏’)
后來覺得還有些東西沒點(diǎn)到,這邊補(bǔ)充補(bǔ)充。
一、回到 HttpServlet 的 service方法
Servlet 基礎(chǔ)接口定義了用于客戶端請(qǐng)求處理的service方法。 當(dāng)請(qǐng)求到達(dá)Servlet容器,由Servlet容器路由到一個(gè)Servlet實(shí)例。
比如說?javax.servlet.http.HttpServlet?類 ,其中有一個(gè)?protected?void service 方法如下:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | private static final String METHOD_DELETE = "DELETE"; private static final String METHOD_HEAD = "HEAD"; private static final String METHOD_GET = "GET"; private static final String METHOD_OPTIONS = "OPTIONS"; private static final String METHOD_POST = "POST"; private static final String METHOD_PUT = "PUT"; private static final String METHOD_TRACE = "TRACE"; private static final String HEADER_IFMODSINCE = "If-Modified-Since"; private static final String LSTRING_FILE = ????????"javax.servlet.http.LocalStrings"; private static ResourceBundle lStrings = ????????ResourceBundle.getBundle(LSTRING_FILE); /** ?* HTTP狀態(tài)碼304 ?*/ public static final int SC_NOT_MODIFIED = 304; /** ?* 接收來自 public service方法的標(biāo)準(zhǔn)HTTP請(qǐng)求, ?* 并將它們分發(fā)給此類中定義的doXXX方法。 ?*/ protected void service(HttpServletRequest req, HttpServletResponse resp) ????????throws ServletException, IOException { ????// 獲取請(qǐng)求方法名 ????String method = req.getMethod(); ????// 如果是GET請(qǐng)求 ????if (method.equals(METHOD_GET)) { ????????// 上一次修改HttpServletRequest對(duì)象的時(shí)間 ????????long lastModified = getLastModified(req); ????????// 沒有改變 ????????if (lastModified == -1) { ????????????doGet(req, resp); ????????} else { ????????????long ifModifiedSince; ????????????try { ????????????????// 獲取請(qǐng)求頭中服務(wù)器修改時(shí)間 ????????????????ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); ????????????} catch (IllegalArgumentException iae) { ????????????????// 獲取無效 ????????????????ifModifiedSince = -1; ????????????} ????????????// 如果請(qǐng)求頭服務(wù)器修改時(shí)間遲 ????????????if (ifModifiedSince < (lastModified / 1000 * 1000)) { ????????????????// 設(shè)置修改HttpServletResponse對(duì)象的時(shí)間,重新設(shè)置瀏覽器的參數(shù) ??????????????????????????????//maybeSetLastModified(resp, lastModified); ????????????????// 調(diào)用doGet方法 ????????????????????????????????doGet(req, resp); ????????????} else { ????????????????// 304 HTTP狀態(tài)碼 ????????????????resp.setStatus(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 { ????????// 如果沒有被請(qǐng)求到的話 ????????String errMsg = lStrings.getString("http.method_not_implemented"); ????????Object[] errArgs = new Object[1]; ????????errArgs[0] = method; ????????errMsg = MessageFormat.format(errMsg, errArgs); ????????// 501 HTTP狀態(tài)碼 ????????resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); ????} } |
代碼邏輯詳解如下:
1、HttpServlet的?protected?void service方法 用于接受?public?service接收的標(biāo)準(zhǔn)HTTP請(qǐng)求。
也就是說,HttpServlet?重寫了父類?GenericServlet?的?service方法。如圖顯示的是該方法,將從容器獲取的?ServletRequest?和?ServletResponse?對(duì)象強(qiáng)制轉(zhuǎn)化成 用于HTTP處理的HttpServletRequest?和?HttpServletResponse?對(duì)象。然后將兩個(gè)對(duì)象路由給了?HttpServlet的?protected?void service方法(圖中代碼選中處)
?
2、然后根據(jù)請(qǐng)求的方法名,分發(fā)到此類定義的doXXX方法。如果沒有被請(qǐng)求到的話,則返回501?HTTP 狀態(tài)碼。
這樣子仿佛明白了什么,也就是說,如果你在?HelloServlet中重寫了doGet方法,這里分發(fā)到就是HttpServlet的子類HelloServlet的doGet方法。
哦~ 還有,501 HTTP 狀態(tài)碼?—?未實(shí)現(xiàn)(Not implemented)表示服務(wù)器不支持實(shí)現(xiàn)請(qǐng)求所需要的功能。例如,客戶發(fā)出了一個(gè)服務(wù)器不支持的PUT請(qǐng)求。原來如此,所謂死記硬背這些HTTP 狀態(tài)碼有什么用?這樣的記憶才是最有效的。
休息休息,小廣告插一下 :(維持生計(jì),O(∩_∩)O~)
涉及到的代碼都會(huì)在開源項(xiàng)目?servlet-core-learning?。簡介?— Servlet/JSP學(xué)習(xí)積累的例子,是Java EE初學(xué)者及Servlet/JSP核心技術(shù)鞏固的最佳實(shí)踐
大致就是這兩步驟。這就是service的工作流程:
1、接受 public service接收的標(biāo)準(zhǔn)HTTP請(qǐng)求。
2、分發(fā)到定義的doXXX方法
二、GET 請(qǐng)求的處理詳解
上面對(duì)于GET請(qǐng)求代碼處理如下:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 如果是GET請(qǐng)求 if (method.equals(METHOD_GET)) { ????// 上一次修改HttpServletRequest對(duì)象的時(shí)間 ????long lastModified = getLastModified(req); ????// 沒有改變 ????if (lastModified == -1) { ????????doGet(req, resp); ????} else { ????????long ifModifiedSince; ????????try { ????????????// 獲取請(qǐng)求頭中服務(wù)器修改時(shí)間 ????????????ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); ????????} catch (IllegalArgumentException iae) { ????????????// 獲取無效 ????????????ifModifiedSince = -1; ????????} ????????// 如果請(qǐng)求頭服務(wù)器修改時(shí)間遲 ????????if (ifModifiedSince < (lastModified / 1000 * 1000)) { ????????????// 設(shè)置修改HttpServletResponse對(duì)象的時(shí)間,重新設(shè)置瀏覽器的參數(shù) ??????????//maybeSetLastModified(resp, lastModified); ????????????// 調(diào)用doGet方法 ????????????doGet(req, resp); ????????} else { ????????????// 304 HTTP狀態(tài)碼 ????????????resp.setStatus(SC_NOT_MODIFIED); ????????} ????} } |
這里,
1、定義了?getLastModified(req) 方法。用于獲取上一次修改HttpServletRequest對(duì)象的時(shí)間。如果lastModified為默認(rèn)的?–1L,則總是刷新。
這個(gè)getLastModified,是HttpServlet定義了用于支持有條件GET操作。即當(dāng)客戶端通過GET請(qǐng)求獲取資源時(shí),當(dāng)資源自第一次獲取那個(gè)實(shí)際點(diǎn)發(fā)生更改后才再次發(fā)生數(shù)據(jù),否則將使用客戶端緩存的數(shù)據(jù)。
在一些適當(dāng)?shù)膱龊?#xff0c;實(shí)現(xiàn)此方法可以更有效的利用網(wǎng)絡(luò)資源,減少不必要的數(shù)據(jù)發(fā)送。
2、如果getLastModified方法的返回值是一個(gè)正數(shù),那就要分以下兩種情況考慮:
??? (1)如果請(qǐng)求頭沒有包含If-Modified-Since頭字段(應(yīng)該是第一次訪問資源時(shí)候) 或者 其getLastModified返回值比If-Modified-Since頭字段指定時(shí)間新,則調(diào)用doGet返回生成response?和?設(shè)置Last-Modified 消息頭。
??? (2)如果其getLastModified返回值比If-Modified-Since頭字段指定時(shí)間舊,則返回一個(gè)304狀態(tài)給客戶端,表示讓客戶端繼續(xù)使用以前緩存的頁面。
比如說 304 這個(gè)場景我在《?JavaEE 要懂的小事:一、圖解Http協(xié)議?》文章中提到,第一次訪問 百度 首頁時(shí),有些資源會(huì)成功獲取 返回200。再次F5,有些資源或直接調(diào)用客戶端的緩存數(shù)據(jù),則返回304。
三、Servlet線程問題
Servlet容器可以并發(fā)路由多個(gè)請(qǐng)求到 Servlet 的?service方法。為了處理這些請(qǐng)求,Servlet必須在并發(fā)及線程安全問題做好處理。上一篇的《?Servlet必會(huì)必知?》提到定義全局變量會(huì)造成線程安全問題。在開發(fā)Servlet時(shí),考慮線程安全問題提出了一下解決:
1、實(shí)現(xiàn)?SingleThreadModel?接口
????Servlet2.4 已經(jīng)提出不提倡使用。實(shí)現(xiàn)此接口,Servlet容器為每個(gè)新的請(qǐng)求創(chuàng)建一個(gè)單獨(dú)的Servlet實(shí)例。這會(huì)有嚴(yán)重性能問題。
2、同步鎖
??? 使用synchronized關(guān)鍵字,雖然可以保證只有一個(gè)線程可以訪問被保護(hù)區(qū)段,已達(dá)到保證線程安全。但是系統(tǒng)性能及并發(fā)量大大降低。不可取~
3、避免使用實(shí)例變量,即Servlet中全局變量。使用局部變量 (推薦)
??? 方法中的局部變量分配在棧空間,每個(gè)線程有私有的棧空間。因此訪問是線程安全的。
我想到了以下一個(gè)問題:
既然Sevlet的全局變量是線程不安全的,那SpringMVC?Controller?也一樣。那我們?cè)?strong>Controller定義個(gè)?XXXService?變量會(huì)不會(huì)造成線程安全呢?
答:因?yàn)檫@是Spring的一個(gè)Service Bean,是線程安全的,所以可以作為單例使用,不會(huì)造成線程安全。
四、總結(jié)(別忘了點(diǎn)贊哦)
補(bǔ)充文章內(nèi)容要點(diǎn):
HttpServlet service 方法詳解
深入理解 代碼 對(duì)HTTP狀態(tài)碼的運(yùn)用
Servlet的線程安全問題
歡迎點(diǎn)擊我的博客及GitHub — 博客提供RSS訂閱哦
———-?http://www.bysocket.com/?————-?https://github.com/JeffLi1993?———-
轉(zhuǎn)載于:https://www.cnblogs.com/Alandre/p/4779897.html
總結(jié)
以上是生活随笔為你收集整理的图解 深入浅出 JavaWeb:Servlet 再说几句的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: u5oh7a海尔电视是几核芯的
- 下一篇: 驴友、野外探险者必须随身携带北斗盒子吗?