在Java Web中Servlet、請求和響應是最基本的三個對象,在Web容器的管理下,這三者能夠完成基本的HTTP請求處理。
Servlet的作用是為客戶提供服務。servlet的角色是接受一個客戶的請求,再返回一個響應。請求可能非常簡單,例如:給我提供一個歡迎頁面;也可能非常復雜,例如:為當前的購物車結賬,這個請求會帶一些客戶端傳來的參數,servlet需要知道自己如何使用請求中的參數,還需要知道該返回什么樣的響應。
一、Servlet
1. Servlet受容器管理
Java Web服務器處理用戶請求的基本過程:用戶在客戶端點擊一個鏈接,瀏覽器會向Web應用服務器發送一個URL請求,該URL會指向一個servlet;Web容器看出這個請求指向某個servlet A,就會創建兩個對象(HttpServletRequest和HttpServletResponse),並分配或創建一個線程,調用servlet A對應的service方法(上述請求和響應對象作為參數);service根據HTTP請求區分出客戶端發來的是GET還是POST請求,並調用對應的doGet()或doPost()方法;在doGet()或doPost()方法中進行業務邏輯的處理,處理完成后的結果通過響應對象返回寫回給客戶端。
2. Servlet的生命周期
在容器啟動時,XXXServlet在JVM的管理下被實例化為一個對象,這時候它還不是servlet,需要在容器的管理下調用init()方法進行初始化,獲得ServletConfig和ServletContext對象的引用后,才稱為一個真正的Servlet。
1)init()
-
何時調用?servlet實例創建后,並在servlet能為客戶請求提供服務之前,容器需要通過init方法初始化servlet。
-
作用?初始化servlet實例,使之獲得web容器的相關信息
-
是否會被覆蓋?有可能
2)service()
-
何時調用?當一個客戶請求到來時,容器會創建(或從線程池分配一個線程),並調用servlet的service方法
-
作用?這個方法會確定HTTP方法(GET or POST),並調用對應的servlet方法——doGet或doPost;
-
是否會被覆蓋?可以,但是不會
3)doGet()或doPost()
-
何時調用?
-
作用?具體的業務邏輯
-
是否會被覆蓋?至少要覆蓋其中之一
關鍵點:每個請求都在一個單獨的線程中運行!
3. Servlet的繼承體系
-
servlet接口:javax.servlet.Servlet,表示所有的Servlet都有這5個方法,其中init、service和destroy三個方法和servlet的生命周期有關;
-
GenericServlet:javax.servlet.GenericServlet,這是一個抽象類,它實現了開發者需要的大部分基本servlet方法,大多數servlet的“servlet行為”都來自這個類;
-
HttpServlet:javax.servlet.http.HttpServlet,這也是一個抽象類,它實現了自己的service()方法,處理servlet的HTTP特性(service方法不僅僅只處理HTTP請求)。
-
MyTestServlet:這是開發者自己編寫的處理類,一般只需要實現doGet()和doPost()方法。
二、請求和響應
1. ServletRequest的繼承體系
HttpServletRequest的API與HTTP有關,例如:Cookie、首部(Header)和會話(Session)等;
2. ServletResponse的繼承體系
ServletResponse(響應)也是類似,用於幫助servlet給客戶端返回處理結果,而HttpServletResponse增加了HTTP相關的內容(例如:錯誤、cookie和首部)等API。
HttpServletRequest和HttpServletResponse這些都是servlet規范里指定的接口,而web容器開發商(例如tomcat)會負責實現這些接口,例如:HttpServletResponseWrapper和ApplicationHttpResponse等,作為開發者,我們只需要知道,在處理doGet()和doPost方法時,容器會給這個方法傳HttpServletRequest和HttpServletResponse兩個參數。
3. GET和POST的區別
-
POST方法有請求體
-
GET方法的查詢參數直接跟在URL后面,不夠安全;
-
GET請求可以建立書簽,POST請求則不能
-
GET請求是冪等的,POST請求不是(GET請求僅僅用於查詢一些數據,POST請求則用於在服務器上更新數據),在業務上會遇到既需要POST請求,又需要保證請求冪等的情況(例如庫存扣減),這種情況需要我們出具對應的實現方案。參見:
4. HTTP請求的API
-
getHeader(),可以獲取首部信息,例如request.getHeader("User-Agent")可以獲取客戶端的平台和瀏覽器信息。
-
getIntHeader(),如果首部信息中的“key/value”對中的value是int類型的,可以使用這個方法直接獲取值而不需要顯式類型轉換
-
getCookies(),可以獲取與請求相關的cookie
-
getSession(),可以獲取與請求相關的會話
-
getMethod(),可以獲取http方法
-
getInputStream(),可以獲取請求的輸入流
-
求,可以獲取查詢字符串中的數據、對於POST請求,可以獲取請求體中的數據
-
getRemotePort(),獲取客戶端的端口號
-
getServerPort(),獲取服務端接受請求的端口號(請求一開始發送服務端的哪個端口?)
-
getLocalPort(),獲取服務端處理請求的端口號(請求最后是發送到服務端的哪個端口?)
5. HTTP響應的API
大多數情況下,使用響應只是為了向客戶發回數據。會對響應調用兩個方法:setContentType()和getWriter()。在此之后,可以將HTML或其他內容寫入到流。不過,你也可以使用響應設置首部、發送錯誤或增加Cookie。
-
setContentType(),設置響應返回的MIME類型
-
getOutputStream(),獲取HTTP輸出字節流
-
getWriter(),獲取HTTP輸出字符流
-
addCookie(Cookie cookie),給響應首部中增加cookie對象,注意這里不是增加“key/value”對
-
addHeader(),在響應首部中添加一個“key/value”對
-
setHeader(),在響應首部中設置一個“key/value”對;和addHeader()的區別是,如果響應首部中已經有對應的key存在,setHeader()會覆蓋現有的值,而addHeder()會新增一個“key/value”對,使用時需要注意;
-
encodeRedirectURL(),對包含session ID的URL進行編碼。使用場景:在瀏覽器不支持使用cookie跟蹤會話時,可以使用URL重寫(即將URL重定向到另一個URL,而這個URL的后面會帶上session id傳給客戶端,這個URL在返回給客戶端之前需要經過編碼)
6. 重定向和請求派發
-
重定向是讓瀏覽器訪問新的URL完成工作,用戶會在瀏覽器地址欄看到新的URL;
-
請求派發是服務端的工作,是當前servlet委托另外的servlet完成請求,並給客戶端發回響應,用戶的瀏覽器地址欄的URL沒有改變;
原文鏈接:https://mp.weixin.qq.com/s/TTE364IxyqI4Gl9kVEVFXw