Servlet生命週期,工作原理,eclipse第一個Servlet,Servlet線程安全

最近學到了Servlet,現將Servlet生命週期,工作原理總結如下
參考鏈接:
http://www.cnblogs.com/xuekyo/archive/2013/02/24/2924072.html]

Servlet運行過程:

Servlet是由Servlet容器調用的(如Tomcat,也稱爲Web服務器),在Web瀏覽器連上Servlet容器,並向Servlet容器發送http請求(包括請求行,請求頭,請求正文),Web Server解析出想訪問的主機名,web應用,web資源後:
1、Servlet容器先檢查是否已經創建了Servlet實例對象,如果沒有,先創建一個Servlet實例對象,並調用Servlet的init()方法完成對象初始化;如果已經創建了,則直接執行下一步。因爲Servet實例對象僅在第一次訪問時創建,之後無論有多少客戶機再次訪問Servlet,都不會重複創建Servlet實例對象,也不會再次執行init()方法
2、Servlet容器創建一個HttpRequest對象,將Web Client請求的信息封裝到這個對象中
3、Servlet容器創建一個HttpResponse對象
4、Servlet容器調用HttpServlet對象的service方法,把HttpRequest對象與HttpResponse對象作爲參數傳給 HttpServlet 對象。
5、HttpServlet調用HttpRequest對象的有關方法,獲取Http請求信息。
6、HttpServlet調用HttpResponse對象的有關方法,生成響應數據(包括響應行,響應頭,響應正文)
7、Servlet容器把HttpServlet的響應結果傳給Web Client。

Servlet 生命週期

Servlet 加載—>實例化—>服務—>銷燬。
1、init():在Servlet的生命週期中,僅在第一次創建Servlet實例對象並執行一次init()方法,後面無論有多少客戶機訪問Servlet,都不會重複執行init()。Servlet實例對象在兩種情況下會創建
(1)首次訪問Servlet時創建Servlet實例對象,並調用init()方法。
(2)如果在web.xml文件中的servlet元素中配置了load-on-startup元素,則會在web服務器啓動時就創建Servlet實例對象,並調用init()方法。struts框架,就是在web服務器啓動時就創建Servlet
2、service():它是Servlet的核心,負責響應客戶的請求。每當一個客戶請求一個HttpServlet對象,該對象的Service()方法就要調用,而且傳遞給這個方法一個“請求”(ServletRequest)對象和一個“響應”(ServletResponse)對象作爲參數。在HttpServlet中已存在Service()方法。默認的服務功能是調用與HTTP請求的方法相應的do功能。
3、destroy(): 僅執行一次,在服務器端停止且卸載Servlet時執行該方法。當Servlet對象退出生命週期時,負責釋放佔用的資源。一個Servlet在運行service()方法時可能會產生其他的線程,因此需要確認在調用destroy()方法時,這些線程已經終止或完成。

Servlet工作原理

1、Servlet接收和響應客戶請求的過程,首先客戶發送一個請求,Servlet是調用service()方法對請求進行響應的,通過源代碼可見,service()方法中對請求的方式進行了匹配,選擇調用doGet,doPost等這些方法,然後再進入對應的方法中調用邏輯層的方法,實現對客戶的響應。在Servlet接口和GenericServlet中是沒有doGet()、doPost()等等這些方法的,HttpServlet中定義了這些方法,但是都是返回error信息,所以,我們每次定義一個Servlet的時候,都必須實現doGet或doPost等這些方法。

2、每一個自定義的Servlet都必須實現Servlet的接口,Servlet接口中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet接口。而HttpServlet繼承於GenericServlet,因此HttpServlet也實現了Servlet接口。所以我們定義Servlet的時候只需要繼承HttpServlet即可。

3、Servlet接口和GenericServlet是不特定於任何協議的,而HttpServlet是特定於HTTP協議的類,所以HttpServlet中實現了service()方法,並將請求ServletRequest、ServletResponse 強轉爲HttpRequest 和 HttpResponse。

eclipse開發第一個Servlet

步驟:
1、新建web project,TestServlet2,下面新建包com.servlet
2、導入Servlet的源碼
3、新建class文件ServletDemo1,繼承GenericServlet類,在service()方法中寫如下代碼:
注:建class文件需要配置web.xml環境,快捷鍵可以直接建Servlet文件,我採用的是第一種方法

res.getOutputStream.write("Hello World".getBytes());

4、配置web.xml文件,在原來的基礎上增加如下代碼:

  <servlet>
    <servlet-name>ServletDemo1</servlet-name>
    <servlet-class>com.servlet.ServletDemo1</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ServletDemo1</servlet-name>
    <url-pattern>/ServletDemo1</url-pattern>
  </servlet-mapping>

注:servlet元素用於註冊servlet,包含兩個子元素servlet-name和servlet-class,servlet-name用於設置servlet的註冊名稱,servlet-class用於設置servlet的完整類名。
servlet-mapping元素用於映射一個已註冊的servlet的一個對外訪問路徑,包含兩個子元素servlet-name和url-pattern,servlet-name用於指定servlet的註冊名稱,url-pattern用於指定servlet的對外訪問路徑,可以設置多個servlet-mapping的url-pattern,指定多個對外訪問路徑,這樣通過不同的url都可以訪問同一個地址。url也可以使用*通配符,格式只有兩種:
(1)/*
(2)*.擴展名
url爲/時,這個Servlet爲缺省Servlet,tomcat默認有缺省Servlet,當找不到url地址時,就映射到缺省Servlet,如果重寫缺省Servlet,就會跳轉到你重寫之後的Servlet

5、發佈項目到tomcat中,當改動web.xml文件時不需要重新發布項目,服務器會自動重新加載
6、啓動tomcat
7、瀏覽器地址欄中輸入http://localhost:8080/TestServlet2/ServletDemo1,顯示成功

Servlet線程安全

  Servlet體系結構是建立在Java多線程機制之上的,它的生命週期是由Web容器負責的。當客戶端第一次請求某個Servlet時,Servlet容器將會根據web.xml配置文件實例化這個Servlet類。當有新的客戶端請求該Servlet時,一般不會再實例化該Servlet類,也就是有多個線程在使用這個實例。
  這樣,當兩個或多個線程同時訪問同一個Servlet時,可能會發生多個線程同時訪問同一資源的情況,數據可能會變得不一致。所以在用Servlet構建的Web應用時如果不注意線程安全的問題,會使所寫的Servlet程序有難以發現的錯誤。
  要解決問題,有如下方案供選擇:
1、synchronized()鎖,將需要同步的代碼放在Synchronized()代碼塊中,單線程的,現實不適用,一次只能一個Client訪問

public class ServletDemo4 extends HttpServlet {
    int id;
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        synchronized (this) {
            id++;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            response.getOutputStream().write((id+"").getBytes());
        }
    }
}

2、實現SingleThreadModel接口,這個接口爲標誌接口,如果servlet實現了該接口,會確保不會有兩個線程同時執行servlet的service()方法。 servlet容器通過同步化訪問servlet的單實例來保證,也可以通過維持servlet的實例池,對於新的請求會分配給一個空閒的servlet。
但SingleThreadModel不會解決所有的線程安全隱患。例如,會話屬性和靜態變量仍然可以被多線程的多請求同時訪問,即便使用了SingleThreadModel

public class ServletDemo4 extends HttpServlet impliments SingleThreadModel{
    int id;
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        synchronized (this) {
            id++;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            response.getOutputStream().write((id+"").getBytes());
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章