Web基礎瞭解版05-Servlet
Servlet
Servlet?
-
從廣義上來講,Servlet規範是Sun公司制定的一套技術標準,包含與Web應用相關的一系列介面,是Web應用實現方式的巨集觀解決方案。而具體的Servlet容器負責提供標準的實現。
-
從狹義上來講,Servlet指的是javax.servlet.Servlet介面及其子介面,也可以指實現了Servlet介面的實現類。
-
Servlet(Server Applet)作為伺服器端的一個元件,它的本意是“伺服器端的小程式”。
-
Servlet的例項物件由Servlet容器負責建立;
-
Servlet的方法由容器在特定情況下呼叫;
-
Servlet容器會在Web應用解除安裝時銷燬Servlet物件的例項。
-
操作步驟
使用Servlet介面的方式:
① 搭建Web開發環境
② 建立動態Web工程
③ 建立javax.servlet.Servlet介面的實現類:com.atguigu.servlet.MyFirstServlet
④ 在service(ServletRequest, ServletResponse)方法中編寫程式碼
⑤ 在web.xml配置檔案中註冊MyFirstServlet
<!-- 宣告一個Servlet,配置的是Servlet的類資訊 --> <servlet> <!-- 這是Servlet的別名,一個名字對應一個Servlet。相當於變數名 --> <servlet-name>MyFirstServlet</servlet-name> <!-- Servlet的全類名,伺服器會根據全類名找到這個Servlet --> <servlet-class>com.servlet.MyFirstServlet</servlet-class> </servlet> <!-- 建立Servlet的請求對映資訊 --> <servlet-mapping> <!-- Servlet的別名,說明這個Servlet將會響應下面url-pattern的請求 --> <servlet-name>MyFirstServlet</servlet-name> <!-- Servlet響應的請求路徑。如果訪問這個路徑,這個Servlet就會響應 --> <url-pattern>/MyFirstServlet</url-pattern> </servlet-mapping>
說明:
<url-pattern>:這個url-pattern可以配置多個,這時表示的就是訪問這些url都會觸發這個Servlet進行響應,執行瀏覽器,訪問剛才配置的url路徑,Servlet的service方法就會被呼叫。
<url-pattern>中的文字內容必須以 / 或 *. 開始書寫路徑。相當於將資源對映到專案根目錄下形成虛擬的資原始檔。
<servlet-mapping>中的<url-pattern>可以宣告多個,可以通過任意一個都可以訪問。但是開發中一般只會配置一個。
⑥ 在WebContent目錄下建立index.html
⑦ 在index.html中加入超連結 <a href="MyFirstServlet">To Servlet</a>
⑧ 點選超連結測試Servlet
如果配置檔案一旦修改,需要重啟伺服器來重新部署web專案。
Servlet作用
-
接收請求 【解析請求報文中的資料:請求引數】
-
處理請求 【DAO和資料庫互動】
-
完成響應 【設定響應報文】
Servlet生命週期
簡單的敘述生命週期,就是物件在容器中從開始建立到銷燬的過程。
Servlet物件是Servlet容器建立的,生命週期方法都是由容器呼叫的。這裡指的就是Tomcat
① Servlet物件的建立:構造器
-
預設情況下,Servlet容器第一次收到HTTP請求時建立對應Servlet物件。
-
容器之所以能做到這一點是由於我們在註冊Servlet時提供了全類名,容器使用反射技術建立了Servlet的物件。
② Servlet物件初始化:init()
-
Servlet容器建立Servlet物件之後,會呼叫init(ServletConfig config)方法。
-
作用:是在Servlet物件建立後,執行一些初始化操作。例如,讀取一些資原始檔、配置檔案,或建立某種連線(比如:資料庫連線)
-
init()方法只在建立物件時執行一次,以後再接到請求時,就不執行了
-
在javax.servlet.Servlet介面中,public void init(ServletConfig config)方法要求容器將ServletConfig的例項物件傳入,這也是我們獲取ServletConfig的例項物件的根本方法。
③ 處理請求:service()
-
在javax.servlet.Servlet介面中,定義了service(ServletRequest req, ServletResponse res)方法處理HTTP請求。
-
在每次接到請求後都會執行。
-
Servlet的作用,主要在此方法中體現。
-
同時要求容器將ServletRequest物件和ServletResponse物件傳入。
④ Servlet物件銷燬:destroy()
-
伺服器重啟、伺服器停止執行或Web應用解除安裝時會銷燬Servlet物件,會呼叫public void destroy()方法。
-
此方法用於銷燬之前執行一些諸如釋放快取、關閉連線、儲存記憶體資料持久化等操作。
Servlet請求過程
-
第一次請求
-
呼叫構造器,建立物件
-
執行init()方法
-
執行service()方法
-
-
後面請求
-
執行service()方法
-
-
物件銷燬前
-
執行destroy()方法
-
ServletConfig介面
-
ServletConfig介面封裝了Servlet配置資訊,這一點從介面的名稱上就能夠看出來。
-
每一個Servlet都有一個唯一對應的ServletConfig物件,代表當前Servlet的配置資訊。
-
物件由Servlet容器建立,並傳入生命週期方法init(ServletConfig config)中。可以直接獲取使用。
-
代表當前Web應用的ServletContext物件也封裝到了ServletConfig物件中,使ServletConfig物件成為了獲取ServletContext物件的一座橋樑。
-
ServletConfig物件的主要功能
-
獲取Servlet名稱:getServletName()
-
獲取全域性上下文ServletContext物件:getServletContext()
-
獲取Servlet初始化引數:getInitParameter(String) / getInitParameterNames()。
-
ServletContext介面
-
Web容器在啟動時,它會為每個Web應用程式都建立一個唯一對應的ServletContext物件,意思是Servlet上下文,代表當前Web應用。
-
由於一個Web應用程式中的所有Servlet都共享同一個ServletContext物件,所以ServletContext物件也被稱為 application 物件(Web應用程式物件)。
-
物件由Servlet容器在專案啟動時建立,通過ServletConfig物件的getServletContext()方法獲取。在專案解除安裝時銷燬。
-
ServletContext物件的主要功能
① 獲取專案的上下文路徑(帶/的專案名): getContextPath()
② 獲取虛擬路徑所對映的本地真實路徑:getRealPath(String path)
-
虛擬路徑:瀏覽器訪問Web應用中資源時所使用的路徑。
-
本地路徑:資源在檔案系統中的實際儲存路徑。
-
作用:將使用者上傳的檔案通過流寫入到伺服器硬碟中。
③ 獲取WEB應用程式的全域性初始化引數(基本不用)
-
設定Web應用初始化引數的方式是在web.xml的根標籤下加入如下程式碼
<web-app> <!-- Web應用初始化引數 --> <context-param> <param-name>ParamName</param-name> <param-value>ParamValue</param-value> </context-param> </web-app>
獲取Web應用初始化引數
④ 作為域物件共享資料
-
作為最大的域物件在整個專案的不同web資源內共享資料。
-
setAttribute(key,value):以後可以在任意位置取出並使用
-
getAttribute(key):取出設定的value值
實現類
-
實現類體系
-
GenericServlet實現Servlet介面
-
HttpServlet繼承GenericServlet
-
-
建立Servlet的最終方式
-
繼承HttpServlet
-
GenericServlet抽象類
-
GenericServlet對Servlet功能進行了封裝和完善,重寫了init(ServletConfig config)方法,用來獲取ServletConfig物件。此時如果GenericServlet的子類(通常是自定義Servlet)又重寫了init(ServletConfig config)方法有可能導致ServletConfig物件獲取不到,所以子類不應該重寫帶引數的這個init()方法。
-
如果想要進行初始化操作,可以重寫GenericServlet提供的無參的init()方法,這樣就不會影響ServletConfig物件的獲取。
-
將service(ServletRequest req,ServletResponse res)保留為抽象方法,讓使用者僅關心業務實現即可。
HttpServlet抽象類
-
專門用來處理Http請求的Servlet。
-
對GenericServlet進行進一步的封裝和擴充套件,在service(ServletRequest req, ServletResponse res)方法中,將ServletRequest和ServletResponse轉換為HttpServletRequest和HttpServletResponse,根據不同HTTP請求型別呼叫專門的方法進行處理。
-
今後在實際使用中繼承HttpServlet抽象類建立自己的Servlet實現類即可。重寫doGet(HttpServletRequest req, HttpServletResponse resp)和doPost(HttpServletRequest req, HttpServletResponse resp)方法實現請求處理,不再需要重寫service(ServletRequest req, ServletResponse res)方法了。
-
又因為我們業務中get,post的處理方式又都是一樣的,所以我們只需要寫一種方法即可,使用另外一種方法呼叫我們寫好的doXXX方法。web.xml配置與之前相同。
HttpServletRequest介面
-
該介面是ServletRequest介面的子介面,封裝了HTTP請求的相關資訊。
-
瀏覽器請求伺服器時會封裝請求報文交給伺服器,伺服器接受到請求會將請求報文解析生成request物件。
-
由Servlet容器建立其實現類物件並傳入service(HttpServletRequest req, HttpServletResponse res)方法中。
功能
1.使用HttpServletRequest物件獲取請求引數,即瀏覽器向伺服器提交的資料
//一個name對應一個值 String userId = request.getParameter("userId");
//一個name對應一組值 String[] soccerTeams = request.getParameterValues("soccerTeam"); for(int i = 0; i < soccerTeams.length; i++){ System.out.println("team "+i+"="+soccerTeams[i]); }
2.獲取url地址引數
String path = request.getContextPath();//重要 System.out.println("上下文路徑:"+path); System.out.println("埠號:"+request.getServerPort()); System.out.println("主機名:"+request.getServerName()); System.out.println("協議:"+request.getScheme());
3.獲取請求頭資訊
String header = request.getHeader("User-Agent"); System.out.println("user-agent:"+header); String referer = request.getHeader("Referer"); System.out.println("上個頁面的地址:"+referer);//登入失敗,返回登入頁面讓使用者繼續登入
4.請求的轉發
//獲取請求轉發物件 RequestDispatcher dispatcher = request.getRequestDispatcher("success.html"); dispatcher.forward(request, response);//發起轉發
5.向請求域中儲存資料
//將資料儲存到request物件的屬性域中 request.setAttribute("attrName", "attrValueInRequest"); //兩個Servlet要想共享request物件中的資料,必須是轉發的關係 request.getRequestDispatcher("/ReceiveServlet").forward(request, response);
//從request屬性域中獲取資料 Object attribute = request.getAttribute("attrName"); System.out.println("attrValue="+attribute);
HttpServletResponse介面
-
該介面是ServletResponse介面的子介面,封裝了伺服器針對於HTTP響應的相關資訊。(暫時只有伺服器的配置資訊,沒有具體的和響應體相關的內容)
-
由Servlet容器建立其實現類物件,並傳入service(HttpServletRequest req, HttpServletResponse res)方法中。
功能
1.使用PrintWriter物件向瀏覽器輸出資料
//通過PrintWriter物件向瀏覽器端傳送響應資訊 PrintWriter writer = res.getWriter(); writer.write("Servlet response"); writer.close();
-
寫出的資料可以是頁面、頁面片段、字串等
-
當寫出的資料包含中文時,瀏覽器接收到的響應資料就可能有亂碼。為了避免亂碼,可以使用Response物件在向瀏覽器輸出資料前設定響應頭。
2.設定響應頭
response.setHeader("Content-Type", "text/html;charset=UTF-8");
- 設定好以後,會在瀏覽器的響應報文中看到設定的響應頭中的資訊。
3.重定向請求
//注意路徑問題,加上/會失敗,會以主機地址為起始,重定向一般需要加上專案名 response.sendRedirect(“success.html”);
- 通過重定向將頁面的地址交給瀏覽器並設定響應狀態碼為302,瀏覽器會自動進行跳轉。
轉發與重定向
請求轉發
- 第一個Servlet接收到了瀏覽器端的請求,進行了一定的處理,然後沒有立即對請求進行響應,而是將請求“交給下一個Servlet”繼續處理,下一個Servlet處理完成之後對瀏覽器進行了響應。在伺服器內部將請求“交給”其它元件繼續處理就是請求的轉發。
- 轉發的情況下,兩個Servlet可以共享同一個Request物件中儲存的資料。
- 當需要將後臺獲取的資料傳送到JSP上顯示的時候,就可以先將資料存放到Request物件中,再轉發到JSP從屬性域中獲取。此時由於是“轉發”,所以它們二者共享Request物件中的資料。
- 轉發的情況下,可以訪問WEB-INF下的資源。
- 轉發以“/”開始表示專案根路徑,重定向以”/”開始表示主機地址。
//1.使用RequestDispatcher物件封裝目標資源的虛擬路徑 RequestDispatcher dispatcher = request.getRequestDispatcher("/index.html"); //2.呼叫RequestDispatcher物件的forward()方法“前往”目標資源 //[注意:傳入的引數必須是傳遞給當前Servlet的service方法的 //那兩個ServletRequest和ServletResponse物件] dispatcher.forward(request, response); }
請求重定向
- 第一個Servlet接收到了瀏覽器端的請求,進行了一定的處理,然後給瀏覽器一個特殊的響應訊息,這個特殊的響應訊息會通知瀏覽器去訪問另外一個資源,這個動作是伺服器和瀏覽器自動完成的。整個過程中瀏覽器端會發出兩次請求,且在瀏覽器位址列裡面能夠看到地址的改變,改變為下一個資源的地址。
- 重定向的情況下,原Servlet和目標資源之間就不能共享請求域資料了。
- HttpServletResponse代表HTTP響應,物件由Servlet容器建立。
//1.呼叫HttpServletResponse物件的sendRedirect()方法 //2.傳入的引數是目標資源的虛擬路徑 response.sendRedirect("index.html");
對比請求的轉發與重定向
轉發 | 重定向 | |
---|---|---|
瀏覽器感知 | 在伺服器內部完成,瀏覽器感知不到 | 伺服器以302狀態碼通知瀏覽器訪問新地址,瀏覽器有感知 |
瀏覽器位址列 | 不改變 | 改變 |
整個過程傳送請求次數 | 一次 | 兩次 |
執行效率 | 效率高 | 效率低 |
API(或發起者) | Request物件 | Response物件 |
能否共享request物件資料 | 能 | 否 |
WEB-INF下的資源 | 能訪問 | 不能訪問 |
目標資源 | 必須是當前web應用中的資源 | 不侷限於當前web應用 |
說明:預設情況下,瀏覽器是不能訪問伺服器web-inf下的資源的,而伺服器是可以訪問的。
字元編碼問題
解決亂碼的方法:就是統一字元編碼
GET請求亂碼
- GET請求引數是在地址後面的。我們需要修改tomcat的配置檔案。需要在server.xml檔案修改Connector標籤,新增URIEncoding="utf-8"屬性。
- 一旦配置好以後,可以解決當前工作空間中所有的GET請求的亂碼問題。
POST請求亂碼
-
POSTt請求伺服器解析出現問題,解決方法:在獲取引數值之前,設定請求的解碼格式,使其和頁面保持一致。
request.setCharacterEncoding("utf-8");
- POST請求亂碼問題的解決,只適用於當前的操作所在的類中。不能類似於GET請求一樣統一解決。因為請求體有可能會上傳檔案。
響應亂碼
- 向瀏覽器傳送響應的時候,要告訴瀏覽器,我使用的字符集是哪個,瀏覽器就會按照這種方式來解碼
//方法一:
response.setHeader("Content-Type", "text/html;charset=utf-8");
//方法二:
response.setContentType("text/html;charset=utf-8");
路徑設定問題
相對路徑和絕對路徑
相對路徑:虛擬路徑如果不以“/”開始,就是相對路徑,瀏覽器會以當前資源所在的虛擬路徑為基準對相對路徑進行解析,從而生成最終的訪問路徑。
絕對路徑:虛擬路徑以“/”開始,就是絕對路徑。
① 在伺服器端:虛擬路徑最開始的“/”表示當前Web應用的根目錄。只要是服務端解析的絕對路徑,都是以web根目錄為起始的。由伺服器解析的路徑包括:(1) web.xml的配置路徑、(2)request轉發的路徑。
② 在瀏覽器端:虛擬路徑最開始的“/”表示當前主機地址。
例如:連結地址“/Path/dir/b.html”經過瀏覽器解析後為: 相當於http://localhost:8989/Path/dir/b.html
由瀏覽器解析的路徑包括:
(1)重定向操作:response.sendRedirect("/xxx") (2)所有HTML標籤:<a href="/xxx"> 、<form action="/xxx"> 、link、img、script等
-
在瀏覽器端,除了使用絕對路徑之外,我們還可以使用base標籤+相對路徑的方式來確定資源的訪問有效。
-
base標籤影響當前頁面中的所有相對路徑,不會影響絕對路徑。相當於給相對路徑設定了一個基準地址。
<!-- 給頁面中的相對路徑設定基準地址 --> <base href="http://localhost:8080/Test_Path/"/>
總結:
* /的有無
* 有: 採用的是絕對路徑
* 無: 採用的是相對路徑
* /的含義:
* 用伺服器解析代表:當前專案下 (http://localhost:8080/上下文專案名/)
* 用瀏覽器解析代表:當前主機下(http://localhost:8080/)
* /的幾種使用位置:
* 用在轉發:採用伺服器解析 (相對和絕對沒有區別)
* 用在重定向:採用瀏覽器解析
* 相對路徑:response.sendRedirect("pages/user/login_success.html");
* 局對路徑:response.sendRedirect(request.getContextPath()+"/pages/user/login_success.html");
* 用在頁面上:採用瀏覽器解析
* 建議採用絕對路徑去載入靜態資源(css/js/image/video...)
* 方式一:<link href="/上下文專案名/static/css/style.css" type="text/css" rel="stylesheet" >
* 方式二:在head標籤中新增一個base標籤並設定href屬性 ★
* <base href="/BookStore02/"> 在當前頁面中所有的路徑錢統一新增指定字首