1. 程式人生 > >重溫Java Web的技術細節

重溫Java Web的技術細節

[toc] ## 一、背景 - Java Servlet可以說是一項非常久遠的技術了,甚至可以說是Java Web應用的起源。也就是說真正瞭解了這項技術的原理與實現細節,我們就掌握了Java Web的基礎,也對以後能上手基於Java Servlet的框架起到事半功倍的作用。 - 本文旨在重溫與Java Servlet密切相關的一些技術細節。 --- ## 二、請求與響應 - Servlet的核心任務是處理請求Request,並給出響應Response,這也是Servlet存在的真正意義。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200826114905909.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 2.1、Http請求 - 在Java Web應用開發中,99.999%用到的都是Http請求 ```java /** * Servlet處理HTTP GET/POST請求 */ public class HttpServletDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { ... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp { ... } } ``` - HTTP存在GET/POST請求,Servlet是如何分辨的呢? - GET請求,請求的資料會附在URL之後(就是把資料放置在HTTP協議頭中),以?
分割URL和傳輸資料,多個引數用&連線 - POST請求:把提交的資料放置在是HTTP包的Body中。 ```shell ## GET和POST請求的區別 # GET請求 GET /books/?name=computer&num=1 HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Connection: Keep-Alive # 注意最後一行是空行 # POST請求 POST /books/add HTTP/1.1 Host: www.wrox.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1 Content-Type: application/x-www-form-urlencoded Content-Length: 40 Connection: Keep-Alive # 以下是POST請求Body name=computer&num=1 ``` - HttpServlet類中的Service方法根據Http請求的型別GET/POST
分別呼叫doGet或doPost ```java /** * HttpServlet類Service方法 */ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if (ifModifiedSince < lastModified / 1000L * 1000L) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } } ``` > - 對於Http Request請求,我們還需要理解冪等的概念:對同一個系統,使用同樣的條件,一次請求和重複的多次請求對系統資源的影響是一致的。 > - 根據以上定義,我們認為GET請求一般情況下是冪等的(GET請求不會引起服務資源的變更),而POST請求是非冪等的(POST請求會提交資料並造成資料更改,造成不可逆,比如多次POST提交訂單資料)。 - Servlet可以通過API獲取到Http請求的相關資料 |API|備註 | |--|--| |request.getParameter("引數名")| 根據引數名獲取引數值(注意,只能獲取一個值的引數) | |request.getParameterValue("引數名“)| 根據引數名獲取引數值(可以獲取多個值的引數) | |request.getMethod| 獲取Http請求方式 | |request.getHeader("User-Agent")| 獲取用臺瀏覽器資訊 | |request.getCookies| 獲取與請求相關的Cookies | |request.getSession| 獲取與使用者相關的會話 | |...| ... | ### 2.2、Http響應 - 響應是為了向用戶傳送資料,Servlet需要處理的是封裝成Http響應訊息的HttpServletResponse物件。 - Http響應一般返回給瀏覽器HTML頁面,再由瀏覽器解析HTML呈現給使用者。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200826140114822.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 但Http響應傳送HTML頁面並不是全部,比如使用者需要通過網站下載某個檔案,那Http響應需要返回位元組流。 ```java protected void doGet(HttpServletRequest req, HttpServletResponse resp){ // 這是一個位元組流 resp.setContentType("application/octet-stream"); resp.setHeader("Content-disposition", "attachment;filename=" + filename); InputStream fis = this.getClass().getResourceAsStream("download.xlsx"); OutputStream os = resp.getOutputStream(); byte[] bis = new byte[1024]; while (-1 != fis.read(bis)) { os.write(bis); } } ``` - Servlet設定Http響應頭控制瀏覽器的行為 ```java //設定refresh響應頭控制瀏覽器每隔1秒鐘重新整理一次 response.setHeader("refresh", "1"); //可分別通過三種方式禁止快取當前文件內容 response.setDateHeader("expries", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache") // 重定向:收到客戶端請求後,通知客戶端去訪問另外一個web資源 protected void doPost(HttpServletRequest req, HttpServletResponse resp) { // 通過sendRedirect方法實現請求重定向 resp.sendRedirect(req.getContextPath() + "/welcome.jsp"); } // 請求分派 protected void doPost(HttpServletRequest req, HttpServletResponse resp) { req.getRequestDispatcher("/result.jsp").forward(req,resp); } ``` - Servlet與HTTP 狀態碼 |程式碼 |訊息 |描述 | |--|--|--| | 200 | OK | 請求成功。 | | 401 | Unauthorized | 所請求的頁面需要授權 | | 404 | Not Found | 伺服器無法找到所請求的頁面。 | | 500 | Internal Server Error | 未完成的請求。伺服器遇到了一個意外的情況 | | ... | ... | ... | ```java // 返回HTTP 500狀態碼 protected void doPost(HttpServletRequest req, HttpServletResponse resp) { resp.sendError(500); } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200826145258896.png#pic_left) --- ## 三、ServletConfig - 在每個Servlet執行時,有可能需要一些初始化**引數**,比如,檔案使用的編碼,共享的資源資訊等。 - 這些初始化引數可以在 web.xml
檔案中使用一個或多個 <init-param> 元素進行描述配置。當 容器 初始化一個 Servlet 時,會將該 Servlet 的配置資訊封裝,並通過 init(ServletConfig)方法將 ServletConfig 物件的引用傳遞給 Servlet。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827082608464.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 3.1 測試ServletConfig引數 - 在web.xml中進行如下配置: ```xml ``` - 在servlet程式碼中進行如下呼叫: ```java public class HttpServletDemo extends HttpServlet { // 重寫Get請求方法,返回一個表單頁面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/html"); resp.setCharacterEncoding("GBK"); PrintWriter out =resp.getWriter(); out.println(""); out.println("管理員郵箱:"); out.println(getServletConfig().getInitParameter("adminEmail")); out.println("
"); out.println(""); } } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827102147895.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_left) --- ## 四、ServletContext -web應用同樣也需要一些初始化的**引數**,但 相對於每一個Servlet有一個ServletConfig來說,一個Web應用(確切的說是每個JVM)僅有一個ServletContext來存放這些引數,這個ServletContext對Web應用中的每個Servlet都可用。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827103823353.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 4.1 測試ServletContext引數 - 在web.xml中進行如下配置: ```xml ``` - 在servlet程式碼中進行如下呼叫: ```java public class HttpServletDemo extends HttpServlet { // 重寫Get請求方法,返回一個表單頁面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/html"); resp.setCharacterEncoding("GBK"); PrintWriter out =resp.getWriter(); out.println(""); out.println(""); out.println("管理員郵箱:"); out.println(getServletConfig().getInitParameter("adminEmail")); out.println("
"); out.println(""); } } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827105115234.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 注意點: ```java // 在web應用中,以下兩句程式碼等價 getServletContext().getInitParameter("title"); getServletConfig().getServletContext().getInitParameter("title"); ``` ### 4.2、ServletContext屬性 - 通過程式設計的方式,可以給ServletContext繫結屬性,使得web應用中的Servlet物件可以使用該全域性屬性。 ```java // 重寫Post請求方法,提交表單上的資料,並返回結果頁面 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); Integer age = Integer.parseInt(req.getParameter("age")); if (name != null && age != null) { User user = new User(name, age); req.setAttribute("name", name); req.setAttribute("age", age); // 設定提交成功次數屬性,並同步加鎖,防止多執行緒併發異常 synchronized (getServletContext()) { if (user.checkName()) { Long postCount = getServletContext().getAttribute("postCount") == null ? 1L : Long.parseLong(getServletContext().getAttribute("postCount").toString()) + 1L; getServletContext().setAttribute("postCount", postCount); req.setAttribute("result", "登記成功" + "! 總共已有" + postCount + "人數登記!!"); } else { req.setAttribute("result", "登記失敗:名稱中包含非法字元"); } } RequestDispatcher view = req.getRequestDispatcher("/result.jsp"); view.forward(req, resp); } } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827122142887.png#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200827122154566.png#pic_center) - 屬性與引數的區別 | | 屬性 | 引數 | |--|--|--| |設定方法 | setAttribute(String name,Object value) | 只能在web.xml中設定 | |返回型別 | Object | String | |獲取方法 | getAttribute(String name). | getInitParameter(String s)| --- ## 五、屬性的作用域 - 除了ServletContext可以設定屬性外,HttpSession會話、HttpServletRequest請求都可以設定和使用屬性。但這三者的作用域是不同的,可參見下表: | |屬性作用域 | 備註 | |--|--|--| |ServletContext | web應用存活期間都可以設定並使用屬性;應用關閉上下文撤消後相應屬性也會消失 | 各個Servlet都可以使用,但執行緒不安全,一般適在於常量屬性等,比如資料庫連線| |HttpSession| HttpSession應用存活期間都可以設定並使用屬性;會話超時或被人為撤消後相應屬性也會消失 | 與會話相關的都可以使用,比如電商網站的購物車屬性| |HttpServletRequest| 每次請求生成時可設定或使用屬性,直到這個請求在service方法中消失 | 請求中屬性是執行緒安全的,類似於區域性變數| --- ## 六、HttpSession - Session就是為了讓伺服器有能力分辨出不同的使用者。 > 1. 客戶端在第一次請求時沒有攜帶任何sessionId,服務端Servlet容器就會給客戶端建立一個HttpSession物件 儲存在伺服器端,然後給這個物件建立一個sessionID 作為唯一標識。同時 > 這個sessionID還會放在一個cookie裡,通過response返回客戶端。 > 2. 客戶端第二次發出請求,cookie中會攜帶sessionId,servlet容器拿著這個sessionID在伺服器端查詢對應的HttpSession物件,找到後就直接拿出來使用。 > 3. Servlet會為不同的客戶端建立不同的sessionId及session物件,代表不同的狀態。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829075930853.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 6.1 HttpSession的關鍵方法 - 關鍵方法 | 方法 |描述 | |--|--| | getSession()| 獲取Session 物件 | | setAttribute()| 在Session 物件中設定屬性 | | getAttribute()| 在Session 物件中獲取屬性 | | removeAttribute()| 在Session 物件中刪除屬性 | | invalidate()| 使Session 物件失效 | |setMaxInactiveInterval()| 設定Session 物件最大間隔時間(在這段時間內,客戶端未對這個會話有新的請求操作,該會話就會撤消) | ### 6.2 簡易的購物車使用HttpSession - 網站後臺通過會話儲存使用者的購物車狀態,使用者在退出網站後在會話的有效時間段內重新進入網站,購物車狀態不消失。 - web.xml配置: ```xml ``` - 購物網站登入頁面Servlet: ```java /** * 購物網站登入頁面 * * @author zhuhuix * @date 2020-08-28 */ public class HttpServletDemo extends HttpServlet { //static int i=0 ; // 重寫Get請求方法,返回登入表單頁面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { req.setAttribute("title", getServletContext().getInitParameter("title")); req.getRequestDispatcher("/form.jsp").forward(req, resp); } // 重寫Post請求方法,進行登入,登入成功後跳車購物車頁面 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); Integer password = Integer.parseInt(req.getParameter("password")); if (name != null && password != null) { req.getSession().setAttribute("name",name); RequestDispatcher view = req.getRequestDispatcher("/cart.jsp"); view.forward(req, resp); } } } ``` - 購物網站登入頁面檢視: ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

使用者登入

``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829092054435.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 購物車頁面檢視: ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

${name}的購物車

貨物 數量
手機
電腦
書本
``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829092140503.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 購物車的Servlet: ```java /** * 購物車Servlet * * @author zhuhuix * @date 2020-08-29 */ public class CartServlet extends javax.servlet.http.HttpServlet { // 重寫Post請求方法,提交購物車的資料,並返回結果頁面 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 查詢頁面上的購物數量,並放入session中 req.getSession().setAttribute("phoneNumber", req.getParameter("phoneNumber")); req.getSession().setAttribute("pcNumber", req.getParameter("pcNumber")); req.getSession().setAttribute("bookNumber", req.getParameter("bookNumber")); RequestDispatcher view = req.getRequestDispatcher("/result.jsp"); view.forward(req, resp); } } ``` - 購物結果檢視頁面: ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

${name}的購物結果

已購貨物 已購數量
手機 ${phoneNumber}
電腦 ${pcNumber}
書本 ${bookNumber}
``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829092529697.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - HttpSession狀態測試 1. 使用者登入網站後在購物車頁面提交購物結果: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829092835988.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) 2. 使用者退出網站,重新登入 後進入購物車頁面,網站後臺將HttpSession中的資料重新調取到頁面進行顯示: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/202008290930212.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829093034164.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 在web.xml中將會話失效時間改為一分鐘,並重啟網站,進行購物車提交 ```xml ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829093454694.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 退出網站,超過**一分鐘後**重新登入,並檢視購物車情況 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829093622738.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 購物車已空空如也 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200829093921580.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) --- ## 七、監聽器 - 監聽器Listener又稱為監聽者,Listener的設計為開發Servlet應用程式提供了一種快捷的手段,能夠方便地從另一個縱向維度控制程式和資料,正所謂**旁觀者清**。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828083731307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 監聽器採用了觀察者設計模式,監聽範圍包括ServletContext、HttpSession、HttpRequest。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020082809320851.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 7.1 監聽器測試--線上會話數統計 - 在web.xml中進行如下配置: ```xml ``` - 建立監聽器類: ```java /** * HttpSession監聽器測試:統計當前伺服器線上人數 */ public class HttpSessionListenerDemo implements javax.servlet.http.HttpSessionListener { @Override public void sessionCreated(javax.servlet.http.HttpSessionEvent httpSessionEvent) { SessionStatics.increase(); } @Override public void sessionDestroyed(javax.servlet.http.HttpSessionEvent httpSessionEvent) { SessionStatics.decrease(); } } /** * 計數類 */ public class SessionStatics { private static volatile Long count=0L; public static void increase(){ synchronized (SessionStatics.class) { count++; } } public static void decrease(){ synchronized (SessionStatics.class) { count--; } } public static Long getCount(){ return count; } } ``` - 建立使用者登入的servlet程式碼: ```java /** * HttpServlet模擬一個登入頁面 * * @author zhuhuix * @date 2020-08-28 */ public class HttpServletDemo extends HttpServlet { // 重寫Get請求方法,返回一個表單頁面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { req.setAttribute("title", getServletContext().getInitParameter("title")); req.getRequestDispatcher("/form.jsp").forward(req, resp); } // 重寫Post請求方法,提交表單上的資料,並返回結果頁面 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); Integer password = Integer.parseInt(req.getParameter("password")); if (name != null && password != null) { // checkUserAndPassword req.setAttribute("name", name); req.setAttribute("result","登入成功,當前線上人數:"+SessionStatics.getCount()); RequestDispatcher view = req.getRequestDispatcher("/result.jsp"); view.forward(req, resp); } } } ``` - 設計使用者登入與登入結果的檢視頁面: ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

使用者登入

``` ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

登入結果

``` - 結果演示: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828105731199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828105752547.png#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828105821613.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828105840919.png#pic_center) ### 7.2 特殊的監聽器 - HttpSessionBindingListener監聽器可以JavaBean物件感知自己被繫結到Session中和從Session中刪除;且該監聽器不需要在web.xml宣告。 ```java public class User implements javax.servlet.http.HttpSessionBindingListener { private String name; public User(String name){ this.name=name; } @Override public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) { System.out.println(name+"加入session"); } @Override public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) { System.out.println(name+"移出session"); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp){ ... // 測試HttpSessionBindingListener req.getSession().setAttribute("user",new User(name)); } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828114537285.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) --- ## 八、過濾器 - 過濾器Filter允許你攔截請求和響應,通過編寫和配置一個過濾器,可以完成一些全域性性的操作:比如安全驗證、統一編 碼處理、敏感字過濾等。 > - Servlet API中提供了一個Filter介面,開發web應用時,如果編寫的Java類實現了這個介面,則把這個java類稱之為過濾器Filter。 > - 通過Filter技術,開發人員可以實現使用者在訪問某個目標資源之前,對訪問的請求和響應進行攔截。簡單說,就是可以實現web容器對某資源的訪問前截獲進行相關的處理,還可以在某資源向web容器返回響應前進行截獲進行處理。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830142153692.png#pic_center) ### 8.1 過濾器的使用方法 - 定義一個類,實現介面Filter,並 重寫Filter介面類的方法; ```java /** * 過濾器例子 */ public class FilterDemo implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 過濾 System.out.println("攔截過濾..."); // 放行請求 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } } ``` - 配置過濾器攔截路徑。 ```xml ``` ### 8.2 過濾器的執行流程 - 當Web容器接受到一個對資源路徑的請求時,會判斷是否有過濾器與這個資源路徑相關聯。如果有,就把請求交給過濾器進行處理。 - 在過濾器程式中,可以改變請求的內容,或者重新設定請求的頭資訊等操作,然後對請求放行傳送給對應目標資源;當目標資源完成響應後,容器再次會將響應轉發給過濾器,這時候可以對響應的內容進行處理,然後再將響應傳送到客戶端.。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830145339333.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ```java @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 前置過濾 System.out.println("攔截過濾{pre}"); // 放行請求 filterChain.doFilter(servletRequest,servletResponse); // 後置過濾 System.out.println("攔截過濾{post}"); } ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830145555889.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 8.3 過濾器的生命週期 1. 初始化init:伺服器啟動時,進行建立Filter物件,然後呼叫init方法;該過程只執行一次,一般用於載入資源。 2. 攔截過濾doFilter:每一次請求被都會被執行。 3. 銷燬destroy:伺服器正常關閉時,Filter物件會被銷燬。該過程只執行一次,一般用於釋放資源。 ### 8.4 過濾器的攔截配置 - 攔截路徑配置: 1. 攔截具體資源:比如/index.jsp,只有訪問index.jsp資源時,過濾器才會執行。 2. 攔截網站目錄:/demo ,訪問網站的demo目錄下的資源時,過濾器才會執行。 3. 字尾名攔截:比如 *.jsp,訪問所有後綴名為jsp資源時,過濾器會執行。 4. 攔截所有資源:比如/*,訪問網站下所有資源時,過濾器都會執行。 ```xml ``` - 攔截方式配置: 1. REQUEST : 瀏覽器直接請求時,過濾器才會執行。 2. FORWARD: 只有轉發訪問時,過濾器才會執行。 3. INCLUDE:包含訪問資源時,過濾器才會執行。 4. ERROR:錯誤跳轉資源時,過濾器才會執行。 5. ASYNC:非同步訪問資源時,過濾器才會執行。 ```xml ``` ### 8.5 過濾器鏈 - 過濾器可以鏈到一起,一個接一個地執行。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830154033459.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 在web.xml中,哪個過濾器的filter-mapping先配置,則哪個過濾器先執行 ```xml ``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830154924664.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ### 8.5 過濾器的案例--登入驗證 - 專案結構 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830162435220.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - web.xml配置 ```xml ``` - 登入程式 ```java /** * 購物網站登入頁面 * * @author zhuhuix * @date 2020-08-30 */ public class LoginServlet extends HttpServlet { // 重寫Get請求方法,返回一個登入頁面 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { req.setAttribute("title", getServletContext().getInitParameter("title")); req.getRequestDispatcher("/form.jsp").forward(req, resp); } // 重寫Post請求方法,提交使用者名稱與密碼 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); Integer password = Integer.parseInt(req.getParameter("password")); if (name != null && password != null) { // 使用者登入驗證通過後,設定使用者名稱稱屬性 req.getSession().setAttribute("name",name); RequestDispatcher view = req.getRequestDispatcher("/cart.jsp"); view.forward(req, resp); } } } ``` - 登入頁面 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

使用者登入

``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830162635124.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 購物車程式 ```java /** * 購物車Servlet * * @author zhuhuix * @date 2020-08-29 */ public class CartServlet extends javax.servlet.http.HttpServlet { // 重寫Post請求方法,提交購物車的資料,並返回結果頁面 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 查詢頁面上的購物數量,並放入session中 req.getSession().setAttribute("phoneNumber", req.getParameter("phoneNumber")); req.getSession().setAttribute("pcNumber", req.getParameter("pcNumber")); req.getSession().setAttribute("bookNumber", req.getParameter("bookNumber")); RequestDispatcher view = req.getRequestDispatcher("/result.jsp"); view.forward(req, resp); } } ``` - 購物車頁面 ```html <%@ page contentType="text/html;charset=UTF-8" language="java" %>

${name}的購物車

貨物 數量
手機
電腦
書本
``` ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830162812811.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) - 程式測試 1. 訪問購物車頁面:由於使用者未登入,則會跳轉到登入頁面 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830162945155.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) 2. 使用者輸入使用者名稱和密碼後進行登入,登入成功後會跳轉到購物車頁面。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830163047739.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830163119195.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) 3. 使用者進行購物,並提交購物結果 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830163152991.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830163231793.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_center) 4. 退出頁面,在瀏覽器中再次訪問購物車頁面,**可以看到過濾器判斷到使用者會話存在,已處於登入狀態,直接跳轉到購物車頁面**。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200830163312332.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70#pic_