1. 程式人生 > >javaweb學習筆記(三):Servlet

javaweb學習筆記(三):Servlet

Servlet的詳細解讀目錄

Servlet詳解

1.Servlet概述與執行過程

由於客戶端是通過URL地址訪問web伺服器中的資源,所以Servlet程式若想被外界訪問,必須把servlet程式對映到一個URL地址上,這個工作在web.xml檔案中使用<servlet>

元素和<servlet-mapping>元素完成。
<servlet>元素用於註冊Servlet,它包含有兩個主要的子元素:<servlet-name><servlet-class>,分別用於設定Servlet的註冊名稱和Servlet的完整類名。

一個<servlet-mapping>元素用於對映一個已註冊的Servlet的一個對外訪問路徑,它包含有兩個子元素:<servlet-name><url-pattern>,分別用於指定Servlet的註冊名稱和Servlet的對外訪問路徑。

<web-app>
	<servlet>
		<servlet-name>AnyName</servlet-name>
		<servlet-class>HelloServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>AnyName</servlet>
		<url-pattern>/demo/hello</url-pattern>
	</servlet-mapping>
</web-app>

Servlet程式是由WEB伺服器呼叫,web伺服器收到客戶端的Servlet訪問請求後:
  ①Web伺服器首先檢查是否已經裝載並建立了該Servlet的例項物件。如果是,則直接執行第④步,否則,執行第②步。
  ②裝載並建立該Servlet的一個例項物件。
  ③呼叫Servlet例項物件的init()方法。
  ④建立一個用於封裝HTTP請求訊息的HttpServletRequest物件和一個代表HTTP響應訊息的HttpServletResponse物件,然後呼叫Servlet的service()方法並將請求和響應物件作為引數傳遞進去。
  ⑤WEB應用程式被停止或重新啟動之前,Servlet引擎將解除安裝Servlet,並在解除安裝之前呼叫Servlet的destroy()方法。

2.Servlet對映路徑

  1. url-pattern要麼以 / 開頭,要麼以*開頭。
  2. 不能同時使用兩種模糊匹配(/**.do),例如/hello/*.do是非法路徑
  3. 當有輸入的URL有多個servlet同時被匹配的情況下:
    3.1 精確匹配優先。
    3.2 以後綴名結尾的模糊url-pattern優先順序最低!!!(/*高於*.do)

關於預設路徑:
servlet的預設路徑(<url-pattern>/</url-pattern>)是在tomcat伺服器內建的一個路徑。該路徑對應的是一個DefaultServlet(預設Servlet)。這個預設的Servlet的作用是用於解析web應用的靜態資原始檔。
URL輸入http://localhost:8080/Test/index.html
1)到當前Test應用下的web.xml檔案查詢是否有匹配的url-pattern。
2)如果沒有匹配的url-pattern,則交給tomcat的內建的DefaultServlet處理
3)DefaultServlet程式到Test應用的根目錄下查詢是存在一個名稱為index.html的靜態檔案。
4)若找到該檔案,則讀取該檔案內容,返回給瀏覽器;如果找不到,則返回404錯誤頁面。

結論: 先找動態資源,再找靜態資源。

3.Servlet 的生命週期

3.1生命週期方法

Servlet程式的生命週期由tomcat伺服器控制!!
Servlet重要的4個生命週期方法:

  • 構造方法: 建立servlet物件的時候呼叫。預設情況下,第一次訪問servlet的時 候建立servlet物件。只調用1次。證明servlet物件在tomcat是單例項的。
  • init方法(帶參): 建立完servlet物件的時候呼叫。只調用1次。
  • service方法: 每次發出請求時呼叫。呼叫n次。
  • destroy方法: 銷燬servlet物件的時候呼叫。停止伺服器或者重新部署web應用 時銷燬servlet物件。只調用1次。

注:①帶參的init方法,是servlet的生命週期方法,一定會被tomcat伺服器呼叫,且其會預設呼叫無參的init方法。一般不重寫。
②若要編寫初始化程式碼,需覆蓋無參的init方法,在裡面編寫即可。

3.2虛擬碼演示生命週期

  1. 通過對映找到servlet-class的內容,字串:gz.a_servlet.FirstServlet
  2. 通過反射構造FirstServlet物件
    2.1 得到位元組碼物件
    Class clazz = class.forName(“gz.itcast.a_servlet.FirstServlet”);
    2.2 呼叫無引數的構造方法來構造物件
    Object obj = clazz.newInstance(); servlet的構造方法被呼叫
  3. 建立ServletConfig物件,通過反射呼叫init方法
    3.1得到方法物件
    Method m = clazz.getDeclareMethod(“init”,ServletConfig.class);
    3.2 呼叫方法
    m.invoke(obj,config); servlet的init帶參方法被呼叫
  4. 建立request,response物件,通過反射呼叫service方法
    4.1 得到方法物件
    Methodm m=clazz.getDeclareMethod(“service”,HttpServletRequest.class,HttpServletResponse.class);
    4.2 呼叫方法
    m.invoke(obj,request,response); servlet的service方法被呼叫
  5. 當tomcat伺服器停止或web應用重新部署,通過反射呼叫destroy方法
    5.1 得到方法物件
    Method m = clazz.getDeclareMethod(“destroy”,null);
    5.2 呼叫方法
    m.invoke(obj,null); servlet的destroy方法被呼叫

4.自動載入

預設情況下,第一次訪問servlet時建立servlet物件。如果servlet的構造方法或init方法中執行了比較多的邏輯程式碼,那麼使用者第一次訪問sevrlet的時候比較慢。解決辦法就是改變servlet建立物件的時機:提前到載入web應用的時候(tomcat啟動時)!
在servlet的配置資訊中,加上一個即可!注:整數值越大,建立的優先順序越低。

<servlet>
    <servlet-name>LifeDemo</servlet-name>
    <servlet-class>gz.c_life.LifeDemo</servlet-class>
    <!-- 讓servlet物件自動載入 -->
    <load-on-startup>1</load-on-startup> 
  </servlet>

如果WEB應用啟動時就需要啟動某個框架程式,那麼可以把框架程式的啟動程式碼放到一個Servlet的init方法中,併為這個Servlet配置</load-on-startup>。這樣的話,當WEB應用啟動時,框架也將隨之啟動。例如struts框架採用的就是這種啟動方式。

5.併發問題

servlet物件在tomcat伺服器是單例項多執行緒的。

因為servlet是多執行緒的,所以當多個servlet的執行緒同時訪問了servlet的共享資料,如成員變數,可能會引發執行緒安全問題。

解決辦法:把使用到共享資料的程式碼塊進行同步(使用synchronized關鍵字進行同步),建議在servlet類中儘量不要使用成員變數。如果確實要使用成員,必須同步。而且儘量縮小同步程式碼塊的範圍。以避免因為同步而導致併發效率降低。

6.ServletConfig物件

主要是用於載入servlet的初始化引數。在一個web應用可以存在多個ServletConfig物件(一個Servlet對應一個ServletConfig物件)。建立時機為在建立完servlet物件之後,在呼叫init方法之前。獲得物件的途徑為直接從有引數的init方法中得到。
servlet的初始化引數在servlet中配置, 這些引數會在載入web應用的時候(啟動tomcat伺服器),封裝到ServletConfig物件中。
注: servlet的引數只能由當前的這個sevlet獲取!

  <servlet>
    <servlet-name>ConfigDemo</servlet-name>
    <servlet-class>gz.f_config.ConfigDemo</servlet-class>
    <!-- 初始引數: -->
    <init-param>
    	<param-name>path</param-name>
    	<param-value>e:/b.txt</param-value>
    </init-param>
  </servlet>

ServletConfig的API:

java.lang.String 		 getInitParameter(java.lang.String name) 根據引數名獲取引數值
java.util.Enumeration 	 getInitParameterNames()    獲取所有引數名
ServletContext 			 getServletContext()        得到servletContex物件
java.lang.String 		 getServletName()           得到servlet的名稱

7. ServletContext物件

ServletContext物件 ,叫做Servlet的上下文物件。表示一個當前的web應用環境。一個web應用中只有一個ServletContext物件。建立時機為在載入web應用時建立ServletContext物件。獲得物件的途徑為從ServletConfig物件的getServletContext方法得到。

<web-app>
	<!-- 在web-app之下配置web應用引數 -->
	<context-param>
		<param-name>AAA</param-name>
		<param-value>AAA's value</param-value>
	</context-param>
	<context-param>
		<param-name>BBB</param-name>
		<param-value>BBB's value</param-value>
	</context-param>

	<servlet>......</servlet>

	<servlet-mapping>...... </servlet-mapping>
</web-app>			

ServletContext物件的核心API(作用)

  • 得到當前web應用的路徑,用在請求重定向的資原始檔中。
    java.lang.String getContextPath()
  • web應用的初始化引數,可以讓當前web應用的所有servlet獲取!
    java.lang.String getInitParameter(java.lang.String name) java.util.Enumeration getInitParameterNames()
  • 域物件的作用是儲存資料,獲取資料。可以在不同的動態資源之間共享資料。
    四個域物件:
HttpServletRequet 				
ServletContext	:在整個web應用中有效
HttpSession 
PageContext

方法有:

void 					setAttribute(java.lang.String name, java.lan g.Object object)
java.lang.Object 		getAttribute(java.lang.String name)  
void 					removeAttribute(java.lang.String name)  
  • 轉發:如果要使用HttpServletRequest域物件進行資料共享,只能用轉發技術。
    轉發與重定向的區別:
    a)轉發的位址列不會改變;而重定向位址列會改變,變成重定向到的地址。
    b)轉發只能轉發到當前web應用內的資源;重定向可以跳轉到當前web應用,或其他web應用,甚至是外部域名網站。
    c)可以在轉發過程中,把資料儲存到HttpServletRequest域物件中;而不可以在重定向過程中,把資料儲存到HttpServletRequest域物件中。
    RequestDispatcher getRequestDispatcher(java.lang.String path)

轉發:一個web資源收到客戶端請求後,通知伺服器去呼叫另外一個web資源進行處理。
重定向:一個web資源收到客戶端請求後,通知瀏覽器去訪問另外一個web資源。
區別:
RequestDispatcher.forward方法只能將請求轉發給同一個WEB應用中的元件;而HttpServletResponse.sendRedirect 方法還可以重定向到同一個站點上的其他應用程式中的資源,甚至是使用絕對URL重定向到其他站點的資源。

如果傳遞給HttpServletResponse.sendRedirect 方法的相對URL以“/”開頭,它是相對於整個WEB站點的根目錄;如果建立RequestDispatcher物件時指定的相對URL以“/”開頭,它是相對於當前WEB應用程式的根目錄。

呼叫HttpServletResponse.sendRedirect方法重定向的訪問過程結束後,瀏覽器位址列中顯示的URL會發生改變,由初始的URL地址變成重定向的目標URL;呼叫RequestDispatcher.forward 方法的請求轉發過程結束後,瀏覽器位址列保持初始的URL地址不變。

HttpServletResponse.sendRedirect方法對瀏覽器的請求直接作出響應,響應的結果就是告訴瀏覽器去重新發出對另外一個URL的訪問請求;RequestDispatcher.forward方法在伺服器端內部將請求轉發給另外一個資源,瀏覽器只知道發出了請求並得到了響應結果,並不知道在伺服器程式內部發生了轉發行為。

RequestDispatcher.forward方法的呼叫者與被呼叫者之間共享相同的request物件和response物件,它們屬於同一個訪問請求和響應過程;而HttpServletResponse.sendRedirect方法呼叫者與被呼叫者使用各自的request物件和response物件,它們屬於兩個獨立的訪問請求和響應過程。

  • 得到web應用的資原始檔
java.lang.String 			getRealPath(java.lang.String path) 
java.io.InputStream 		getResourceAsStream(java.lang.String path)