Servlet學習筆記


一、Servlet 是什么?

Java Servlet 是運行在 Web 服務器或應用服務器上的程序,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 服務器上的數據庫或應用程序之間的中間層。

使用 Servlet,您可以收集來自網頁表單的用戶輸入,呈現來自數據庫或者其他源的記錄,還可以動態創建網頁。

Java Servlet 通常情況下與使用 CGI(Common Gateway Interface,公共網關接口)實現的程序可以達到異曲同工的效果。但是相比於 CGI,Servlet 有以下幾點優勢:

  • 1、性能明顯更好。
  • 2、Servlet 在 Web 服務器的地址空間內執行。這樣它就沒有必要再創建一個單獨的進程來處理每個客戶端請求。
  • 3、Servlet 是獨立於平台的,因為它們是用 Java 編寫的。
  • 4、服務器上的 Java 安全管理器執行了一系列限制,以保護服務器計算機上的資源。因此,Servlet 是可信的。
  • 5、Java 類庫的全部功能對 Servlet 來說都是可用的。它可以通過 sockets 和 RMI 機制與 applets、數據庫或其他軟件進行交互。

二、Servlet的生命周期

Servlet 生命周期可被定義為從創建直到毀滅的整個過程。以下是 Servlet 遵循的過程:

  • 1、Servlet 通過調用 init () 方法進行初始化。
  • 2、Servlet 調用 service() 方法來處理客戶端的請求。
  • 3、Servlet 通過調用 destroy() 方法終止(結束)。
  • 4、最后,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的。

init() 方法

init 方法被設計成只調用一次。它在第一次創建 Servlet 時被調用,在后續每次用戶請求時不再調用。因此,它是用於一次性初始化,就像 Applet 的 init 方法一樣。

Servlet 創建於用戶第一次調用對應於該 Servlet 的 URL 時,但是您也可以指定 Servlet 在服務器第一次啟動時被加載。

service() 方法

service() 方法是執行實際任務的主要方法。Servlet 容器(即 Web 服務器)調用 service() 方法來處理來自客戶端(瀏覽器)的請求,並把格式化的響應寫回給客戶端。

每次服務器接收到一個 Servlet 請求時,服務器會產生一個新的線程並調用服務。service() 方法檢查 HTTP 請求類型(GET、POST、PUT、DELETE 等),並在適當的時候調用 doGet、doPost、doPut,doDelete 等方法。

 destroy() 方法

destroy() 方法只會被調用一次,在 Servlet 生命周期結束時被調用。destroy() 方法可以讓您的 Servlet 關閉數據庫連接、停止后台線程、把 Cookie 列表或點擊計數器寫入到磁盤,並執行其他類似的清理活動。

在調用 destroy() 方法之后,servlet 對象被標記為垃圾回收。

實例:

 1 package gz.itcast.b_request;
 2 import java.io.IOException;
 3 
 4 import javax.servlet.ServletConfig;
 5 import javax.servlet.ServletException;
 6 import javax.servlet.ServletRequest;
 7 import javax.servlet.ServletResponse;
 8 import javax.servlet.http.HttpServlet;
 9 
10 
11 
12 public class LifeDemo1 extends HttpServlet {
13 
14     /**
15      * 1.構造方法
16      */
17     public LifeDemo1() {
18         System.out.println("1.servlet對象被創建了。");
19     }
20 
21     /**
22      * 2.init方法
23      */
24     @Override
25     public void init(ServletConfig config) throws ServletException {
26         System.out.println("2.init方法被調用");
27     }
28 
29     /**
30      * 3.service方法
31      */
32     @Override
33     public void service(ServletRequest req, ServletResponse res)
34             throws ServletException, IOException {
35         System.out.println("3.service方法被調用");
36     }
37 
38     /**
39      * 4.destroy方法
40      */
41     @Override
42     public void destroy() {
43         System.out.println("4.servlet對象銷毀了");
44     }
45 }

執行后:

以后繼續請求時:

可見,就緒請求時只有service()方法執行!

三、怎樣理解Servlet的單實例多線程?

不同的用戶同時對同一個業務(如注冊)發出請求,那這個時候容器里產生的有是幾個servlet實例呢?

答案是:只有一個servlet實例。一個servlet是在第一次被訪問時加載到內存並實例化的。同樣的業務請求共享一個servlet實例。不同的業務請求一般對應不同的servlet。

由於Servlet/JSP默認是以多線程模式執行的,所以,在編寫代碼時需要非常細致地考慮多線程的安全性問題。

 JSP的中存在的多線程問題: 

當客戶端第一次請求某一個JSP文件時,服務端把該JSP編譯成一個CLASS文件,並創建一個該類的實例,然后創建一個線程處理CLIENT端的請求。如果有多個客戶端同時請求該JSP文件,則服務端會創建多個線程。每個客戶端請求對應一個線程。以多線程方式執行可大大降低對系統的資源需求,提高系統的並發量及響應時間。、

 對JSP中可能用的的變量說明如下: 

實例變量: 實例變量是在堆中分配的,並被屬於該實例的所有線程共享,所以不是線程安全的。 
JSP系統提供的8個類變量 
JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是線程安全的(因為每個線程對應的request,respone對象都是不一樣的,不存在共享問題), APPLICATION在整個系統內被使用,所以不是線程安全的。

局部變量: 局部變量在堆棧中分配,因為每個線程都有它自己的堆棧空間,所以是線程安全的
靜態類: 靜態類不用被實例化,就可直接使用,也不是線程安全的

外部資源: 在程序中可能會有多個線程或進程同時操作同一個資源(如:多個線程或進程同時對一個文件進行寫操作).此時也要注意同步問題. 

Servlet單實例多線程機制:

Servlet采用多線程來處理多個請求同時訪問。servlet依賴於一個線程池來服務請求。線程池實際上是一系列的工作者線程集合。Servlet使用一個調度線程來管理工作者線程。 
        當容器收到一個Servlet請求,調度線程從線程池中選出一個工作者線程,將請求傳遞給該工作者線程,然后由該線程來執行Servlet的service方法。當這個線程正在執行的時候,容器收到另外一個請求,調度線程同樣從線程池中選出另一個工作者線程來服務新的請求,容器並不關心這個請求是否訪問的是同一個Servlet.當容器同時收到對同一個Servlet的多個請求的時候,那么這個Servlet的service()方法將在多線程中並發執行。 
         Servlet容器默認采用單實例多線程的方式來處理請求,這樣減少產生Servlet實例的開銷,提升了對請求的響應時間,對於Tomcat可以在server.xml中通過<Connector>元素設置線程池中線程的數目。 

四、如何開發線程安全的Servlet

      1、實現 SingleThreadModel 接口 

  該接口指定了系統如何處理對同一個Servlet的調用。如果一個Servlet被這個接口指定,那么在這個Servlet中的service方法將不會有兩個線程被同時執行,當然也就不存在線程安全的問題。這種方法只要將前面的Concurrent Test類的類頭定義更改為: 

Public class Concurrent Test extends HttpServlet implements SingleThreadModel { 
………… 
}  

 2、同步對共享數據的操作 
  使用synchronized 關鍵字能保證一次只有一個線程可以訪問被保護的區段

   3、避免使用實例變量 

本實例中的線程安全問題是由實例變量造成的,只要在Servlet里面的任何方法里面都不使用實例變量,那么該Servlet就是線程安全的。 

PS:

1) Struts2的Action是原型,非單實例的;會對每一個請求,產生一個Action的實例來處理。 

Struts1 Action是單實例的,spring mvc的controller也是如此。因此開發時要求必須是線程安全的,因為僅有Action的一個實例來處理所有的請求。單例策略限制了Struts1 Action能作的事,並且要在開發時特別小心。Action資源必須是線程安全的或同步的。
2) Struts1的Action,Spring的Ioc容器管理的bean 默認是單實例的.

 Spring的Ioc容器管理的bean 默認是單實例的。

Struts2 Action對象為每一個請求產生一個實例,因此沒有線程安全問題。(實際上,servlet容器給每個請求產生許多可丟棄的對象,並且不會導致性能和垃圾回收問題)。

當Spring管理Struts2的Action時,bean默認是單實例的,可以通過配置參數將其設置為原型。(scope="prototype )

 五、servlet與jsp的區別

1.jsp經編譯后就變成了Servlet.(JSP的本質就是Servlet,JVM只能識別java的類,不能識別JSP的代碼,Web容器將JSP的代碼編譯成JVM能夠識別的java類)
2.jsp更擅長表現於頁面顯示,servlet更擅長於邏輯控制.
3.Servlet中沒有內置對象,內置對象都是必須通過HttpServletRequest對象,HttpServletResponse對象以及HttpServlet對象得到.Jsp是Servlet的一種簡化,使用Jsp只需要完成程序員需要輸出到客戶端的內容,Jsp中的Java腳本如何鑲嵌到一個類中,由Jsp容器完成。而Servlet則是個完整的Java類,這個類的Service方法用於生成對客戶端的響應。
4.對於靜態HTML標簽,Servlet都必須使用頁面輸出流逐行輸出

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM