標籤:method 斜杠 ges 動態 doc 部署 介面 不能 https
【原文】https://www.toutiao.com/i6594316694657696264/
解析Java Servlet工作過程
Servlet簡介
- Servlet是sun公司提供的一門用於開發動態web資源的技術。
- Sun公司在其API中提供了一個servlet介面,使用者若想用發一個動態web資源(即開發一個Java程式向瀏覽器輸出資料),需要完成以下2個步驟:
- 編寫一個Java類,實現servlet介面。
- 把開發好的Java類部署到web伺服器中。
Servlet的運行過程
- Servlet程式是由WEB伺服器調用,web伺服器收到用戶端的Servlet訪問請求後:
- ①Web伺服器首先檢查是否已經裝載並建立了該Servlet的執行個體對象。如果是,則直接執行第④步,否則,執行第②步。
- ②裝載並建立該Servlet的一個執行個體對象。
- ③調用Servlet執行個體對象的init()方法。
- ④建立一個用於封裝HTTP請求訊息的HttpServletRequest對象和一個代表HTTP響應訊息的HttpServletResponse對象,然後調用Servlet的service()方法並將請求和響應對象作為參數傳遞進去。
- ⑤WEB應用程式被停止或重新啟動之前,Servlet引擎將卸載Servlet,並在卸載之前調用Servlet的destroy()方法。
Servlet介面實作類別
- Servlet介面SUN公司定義了兩個預設實作類別,分別為:GenericServlet、HttpServlet。
- HttpServlet指能夠處理HTTP請求的servlet,它在原有Servlet介面上添加了一些與HTTP協議處理方法,它比Servlet介面的功能更為強大。因此開發人員在編寫Servlet時,通常應繼承這個類,而避免直接去實現Servlet介面。
- HttpServlet在實現Servlet介面時,覆寫了service方法,該方法體內的代碼會自動判斷使用者的請求方式,如為GET請求,則調用HttpServlet的doGet方法,如為Post請求,則調用doPost方法。因此,開發人員在編寫Servlet時,通常只需要覆寫doGet或doPost方法,而不要去覆寫service方法。
- 通過Eclipse建立和編寫Servlet
- 在編寫Servlet類的時候要繼承javax.servlet.http.HttpServlet類。
- eclipse自動產生代碼如下:
package gacl.servlet.study;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; public class ServletDemo1 extends HttpServlet { /** * The doGet method of the servlet. <br> * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" This is "); out.print(this.getClass()); out.println(", using the GET method"); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } /** * The doPost method of the servlet. <br> * * This method is called when a form has its tag value method equals to post. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" This is "); out.print(this.getClass()); out.println(", using the POST method"); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } }
- 如果是post請求的話則編寫dopost函數,是get請求的話就編寫doget函數。但此時web伺服器還不能調用該servlet類,還需在web.xml檔案中配置。
Servlet開發注意細節
- Servlet訪問URL映射配置
- 由於用戶端是通過URL地址訪問web伺服器中的資源,所以Servlet程式若想被外界訪問,必須把servlet程式映射到一個URL地址上,這個工作在web.xml檔案中使用元素和元素完成。
- 元素用於註冊Servlet,它包含有兩個主要的子項目:和,分別用於設定Servlet的註冊名稱和Servlet的完整類名。
- 一個元素用於映射一個登入的Servlet的一個對外訪問路徑,它包含有兩個子項目:和,分別用於指定Servlet的註冊名稱和Servlet的對外訪問路徑。
- 同一個Servlet可以被映射到多個URL上,即多個元素的子項目的設定值可以是同一個Servlet的註冊名。
- Servlet訪問URL使用*萬用字元映射
- 在Servlet映射到的URL中也可以使用萬用字元,但是只能有兩種固定的格式:一種格式是”.副檔名”,另一種格式是以正斜杠(/)開頭並以”/*”結尾。例如:
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/*</url-pattern>
- 可以匹配任意的字元,所以此時可以用任意的URL去訪問ServletDemo1這個Servlet.例如:
http://localhost:8080/工程名/nvlkfmndlmd
對於如下的一些映射關係:
Servlet1 映射到 /abc/
Servlet2 映射到 /
Servlet3 映射到 /abc
Servlet4 映射到 .do
問題:
當請求URL為“/abc/a.html”,“/abc/”和“/”都匹配,哪個servlet響應
Servlet引擎將調用Servlet1。
當請求URL為“/abc”時,“/abc/”和“/abc”都匹配,哪個servlet響應
Servlet引擎將調用Servlet3。
當請求URL為“/abc/a.do”時,“/abc/”和“.do”都匹配,哪個servlet響應
Servlet引擎將調用Servlet1。
當請求URL為“/a.do”時,“/”和“.do”都匹配,哪個servlet響應
Servlet引擎將調用Servlet2。
當請求URL為“/xxx/yyy/a.do”時,“/”和“.do”都匹配,哪個servlet響應
Servlet引擎將調用Servlet2。
匹配的原則就是”誰長得更像就找誰”
- Servlet與普通Java類的區別
- Servlet是一個供其他Java程式(Servlet引擎)調用的Java類,它不能獨立運行,它的運行完全由Servlet引擎來控制和調度。
- 針對用戶端的多次Servlet請求,通常情況下,伺服器只會建立一個Servlet執行個體對象,也就是說Servlet執行個體對象一旦建立,它就會駐留在記憶體中,為後續的其它請求服務,直至web容器退出,servlet執行個體對象才會銷毀。
- 在Servlet的整個生命週期內,Servlet的init方法只被調用一次。而對一個Servlet的每次訪問請求都導致Servlet引擎調用一次servlet的service方法。對於每次訪問請求,Servlet引擎都會建立一個新的HttpServletRequest請求對象和一個新的HttpServletResponse響應對象,然後將這兩個對象作為參數傳遞給它調用的Servlet的service()方法,service方法再根據請求方式分別調用doXXX方法。
- 如果在元素中配置了一個元素,那麼WEB應用程式在啟動時,就會裝載並建立Servlet的執行個體對象、以及調用Servlet執行個體對象的init()方法。
- 舉例:
<servlet><servlet-name>invoker</servlet-name><servlet-class>org.apache.catalina.servlets.InvokerServlet</servlet-class><load-on-startup>1</load-on-startup></servlet>
- 用途:為web應用寫一個InitServlet,這個servlet配置為啟動時裝載,為整個web應用建立必要的資料庫表和資料。
- 預設Servlet
- 如果某個Servlet的映射路徑僅僅為一個正斜杠(/),那麼這個Servlet就成為當前Web應用程式的預設Servlet。
- 凡是在web.xml檔案中找不到匹配的元素的URL,它們的訪問請求都將交給預設Servlet處理,也就是說,預設Servlet用於處理所有其他Servlet都不處理的訪問請求。例如:
<!-- 將ServletDemo2配置成預設Servlet -->8 <servlet-mapping>9 <servlet-name>ServletDemo2</servlet-name>10 <url-pattern>/</url-pattern>11 </servlet-mapping>
- 3.當訪問不存在的Servlet時,就使用配置的預設預設的Servlet進行處理
- 在confweb.xml檔案中,註冊了一個名稱為org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個Servlet設定為了預設Servlet。
- 當訪問Tomcat伺服器中的某個靜態HTML檔案和圖片時,實際上是在訪問這個預設Servlet。
- 當多個用戶端並發訪問同一個Servlet時,web伺服器會為每一個用戶端的訪問請求建立一個線程,並在這個線程上調用Servlet的service方法,因此service方法內如果訪問了同一個資源的話,就有可能引發安全執行緒問題。
- 安全執行緒的例子:
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {/*** 當多線程並發訪問這個方法裡面的代碼時,會存線上程安全問題嗎?* i變數被多個線程並發訪問,但是沒有安全執行緒問題,因為i是doGet方法裡面的局部變數,* 當有多個線程並發訪問doGet方法時,每一個線程裡面都有自己的i變數,* 各個線程操作的都是自己的i變數,所以不存線上程安全問題* 多線程並發訪問某一個方法的時候,如果在方法內部定義了一些資源(變數,集合等)* 那麼每一個線程都有這些東西,所以就不存線上程安全問題了*/int i=1;i++;response.getWriter().write(i);}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}把i定義成全域變數,當多個線程並發訪問變數i時,就會存線上程安全問題了。安全執行緒問題只存在多個線程並行作業同一個資源的情況下,所以在編寫Servlet的時候,如果並發訪問某一個資源(變數,集合等),就會存線上程安全問題。那麼該如何解決這個問題呢?先看下面的代碼:public class ServletDemo3 extends HttpServlet {int i=1;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {/*** 加了synchronized後,並發訪問i時就不存線上程安全問題了,* 為什麼加了synchronized後就沒有安全執行緒問題了呢?* 假如現在有一個線程訪問Servlet對象,那麼它就先拿到了Servlet對象的那把鎖* 等到它執行完之後才會把鎖還給Servlet對象,由於是它先拿到了Servlet對象的那把鎖,* 所以當有別的線程來訪問這個Servlet對象時,由於鎖已經被之前的線程拿走了,後面的線程只能排隊等候了**/synchronized (this) {//在java中,每一個對象都有一把鎖,這裡的this指的就是Servlet對象i++;try {Thread.sleep(1000*4);} catch (InterruptedException e) {e.printStackTrace();}response.getWriter().write(i+"");}}
5.現在這種做法是給Servlet對象加了一把鎖,保證任何時候都只有一個線程在訪問該Servlet對象裡面的資源,這樣就不存線上程安全問題了.
這種做法雖然解決了安全執行緒問題,但是編寫Servlet卻萬萬不能用這種方式處理安全執行緒問題,假如有9999個人同時訪問這個Servlet,那麼這9999個人必須按先後順序排隊輪流訪問。
針對Servlet的安全執行緒問題,Sun公司是提供有解決方案的:讓Servlet去實現一個SingleThreadModel介面,如果某個Servlet實現了SingleThreadModel介面,那麼Servlet引擎將以單線程模式來調用其service方法。
查看Sevlet的API可以看到,SingleThreadModel介面中沒有定義任何方法和常量,在Java中,把沒有定義任何方法和常量的介面稱之為標記介面,經常看到的一個最典型的標記介面就是”Serializable”,這個介面也是沒有定義任何方法和常量的,標記介面在Java中有什麼用呢?主要作用就是給某個對象打上一個標誌,告訴JVM,這個對象可以做什麼,比如實現了”Serializable“介面的類的對象就可以被序列化,還有一個”Cloneable“介面,這個也是一個標記介面,在預設情況下,Java中的對象是不允許被複製的,就像現實生活中的人一樣,不允許複製,但是只要實現了”Cloneable”介面,那麼對象就可以被複製了。
讓Servlet實現了SingleThreadModel介面,只要在Servlet類的定義中增加實現SingleThreadModel介面的聲明即可。
對於實現了SingleThreadModel介面的Servlet,Servlet引擎仍然支援對該Servlet的多線程並發訪問,其採用的方式是產生多個Servlet執行個體對象,並發的每個線程分別調用一個獨立的Servlet執行個體對象。
實現SingleThreadModel介面並不能真正解決Servlet的安全執行緒問題,因為Servlet引擎會建立多個Servlet執行個體對象,而真正意義上解決多安全執行緒問題是指一個Servlet執行個體對象被多個線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標 記為Deprecated(過時的)。
- 一般來說,servlet是單例的,同一個執行個體可以同時有多個使用者訪問,這個沒有任何問題。問題在於servlet是否有狀態,對這些狀態的訪問是否必須是synchronized的。如果是,那麼在同一個時間就只有一個使用者可以訪問這些狀態了,這就大大降低了效能。所以一般來說servlet都是無狀態的。
【轉】Java學習---解析Java Servlet工作過程