由來 Servlet 是在
伺服器 上運行的小程式。這個詞是在 Java
applet 的環境中創造的,Java applet 是一種當作單獨檔案跟網頁一起傳送的小程式,它通常用於在客戶端運行,結果得到為用戶進行運算或者根據用戶互作用定點陣圖形等服務。
伺服器上需要一些程式,常常是根據用戶輸入訪問資料庫的程式。這些通常是使用
公共網關接口 (
C ommon
G ateway
I nterface,CGI)應用程式完成的。然而,在伺服器上運行 Java,這種程式可使用 Java 程式語言實現。在通信量大的伺服器上,JavaServlet 的優點在於它們的執行速度更快於 CGI 程式。各個用戶請求被激活成單個程式中的一個執行緒,而
無需 創建單獨的進程,這意味著
伺服器 端處理請求的
系統開銷 將明顯降低。
實現過程
最早支持 Servlet 技術的是 JavaSoft 的 Java Web Server。此後,一些其它的基於 Java 的 Web Server 開始支持標準的 Servlet API。Servlet 的主要功能在於互動式地瀏覽和修改數據,生成動態 Web 內容。這個過程為:
客戶端傳送請求至伺服器端;
伺服器將請求信息傳送至 Servlet;
Servlet 生成回響內容並將其傳給
伺服器 。回響內容動態生成,通常取決於客戶端的請求;
伺服器將回響返回給客戶端。
Servlet 看起來像是通常的 Java 程式。Servlet 導入特定的屬於 Java Servlet API 的包。因為是對象
位元組碼 ,可動態地從網路載入,可以說 Servlet 對 Server 就如同 Applet對 Client 一樣,但是,由於 Servlet 運行於 Server 中,它們並不需要一個
圖形用戶界面 。從這個角度講,Servlet 也被稱為 FacelessObject。
一個 Servlet 就是 Java 程式語言中的一個類,它被用來擴展
伺服器 的性能,伺服器上駐留著可以通過“請求-回響”編程模型來訪問的應用程式。雖然 Servlet 可以對任何類型的請求產生回響,但通常只用來擴展 Web 伺服器的應用程式。
目前最新版本為 4.0。
命名 Servlet 的命名可以看出 sun 命名的特點,如 Applet 表示小應用程式;Scriptlet = Script + Applet,表示小腳本程式;同樣 Servlet = Service + Applet,表示小服務程式。
生命周期 客戶端請求該 Servlet;
載入 Servlet 類到記憶體;
實例化並調用init()方法初始化該 Servlet;
service()(根據請求方法不同調用doGet() 或者 doPost(),此外還有doHead()、doPut()、doTrace()、doDelete()、doOptions()、destroy())。
載入和實例化 Servlet。這項操作一般是動態執行的。然而,Server 通常會提供一個管理的選項,用於在 Server 啟動時強制裝載和初始化特定的 Servlet。
Server 創建一個 Servlet的實例
第一個客戶端的請求到達 Server
Server 調用 Servlet 的 init() 方法(可配置為 Server 創建 Servlet 實例時調用,在 web.xml 中 <servlet> 標籤下配置 <load-on-startup> 標籤,配置的值為整型,值越小 Servlet 的啟動優先權越高)
一個客戶端的請求到達 Server
Server 創建一個請求對象,處理客戶端請求
Server 創建一個回響對象,回響客戶端請求
Server 激活 Servlet 的 service() 方法,傳遞請求和回響對象作為參數
service() 方法獲得關於請求對象的信息,處理請求,訪問其他資源,獲得需要的信息
service() 方法使用回響對象的方法,將回響傳回Server,最終到達客戶端。service()方法可能激活其它方法以處理請求,如 doGet() 或 doPost() 或程式設計師自己開發的新的方法。
對於更多的客戶端請求,Server 創建新的請求和回響對象,仍然激活此 Servlet 的 service() 方法,將這兩個對象作為
參數傳遞 給它。如此重複以上的循環,但無需再次調用 init() 方法。一般 Servlet 只初始化一次(
只有一個對象 ),當 Server 不再需要 Servlet 時(一般當 Server 關閉時),Server 調用 Servlet 的 destroy() 方法。
下圖顯示了一個典型的 Servlet 生命周期方案:
典型的 Servlet 生命周期 1.第一個到達伺服器的 HTTP 請求被委派到 Servlet 容器。
2.Servlet 容器在調用 service() 方法之前載入 Servlet。
3.然後 Servlet 容器處理由多個執行緒產生的多個請求,每個執行緒執行一個單一的 Servlet 實例的 service() 方法。
工作模式 伺服器啟動並調用 Servlet,Servlet 根據客戶端請求生成回響內容並將其傳給伺服器
伺服器將回響返回客戶端
比較 與 Applet 的比較
相似之處:
* 它們不是獨立的應用程式,沒有
main () 方法。
* 它們不是由用戶或程式設計師調用,而是由另外一個應用程式(容器)調用。
* 它們都有一個生存周期,包含 init() 和 destroy() 方法。
不同之處:
* Servlet 則沒有圖形界面,運行在
伺服器 端。
與 CGI 比較
與傳統的 CGI 和許多其他類似 CGI 的技術相比,Java Servlet 具有更高的效率,更容易使用,功能更強大,具有更好的可移植性,更節省投資。在未來的技術發展過程中,Servlet 有可能徹底取代 CGI。
在傳統的 CGI中,每個請求都要啟動一個新的進程,如果 CGI 程式本身的執行時間較短,啟動進程所需要的開銷很可能反而超過實際執行時間。而在 Servlet 中,每個請求由一個輕量級的 Java 執行緒處理(而不是重量級的作業系統進程)。
在傳統 CGI 中,如果有 N 個並發的對同一 CGI程式的請求,則該CGI程式的代碼在記憶體中重複裝載了 N 次;而對於 Servlet,處理請求的是 N 個執行緒,只需要一份 Servlet 類代碼。在性能最佳化方面,Servlet 也比 CGI 有著更多的選擇。
* 方便
Servlet 提供了大量的實用工具例程,例如自動地解析和解碼 HTML 表單數據、讀取和設定
HTTP 頭、處理
Cookie 、跟蹤會話狀態等。
* 功能強大
在Servlet中,許多使用傳統 CGI 程式很難完成的任務都可以輕鬆地完成。例如,Servlet 能夠直接和 Web
伺服器 互動,而普通的 CGI 程式不能。Servlet 還能夠在各個程式之間共享數據,使得資料庫
連線池 之類的功能很容易實現。
* 可移植性好
Servlet 用 Java 編寫,Servlet
API 具有完善的標準。因此,為 IPlanet Enterprise Server 寫的 Servlet 無需任何實質上的改動即可移植到
Apache 、
Microsoft IIS 或者 WebStar。幾乎所有的主流
伺服器 都直接或通過
外掛程式 支持 Servlet。
* 節省投資
不僅有許多廉價甚至免費的 Web 伺服器可供個人或小規模網站使用,而且對於現有的伺服器,如果它不支持 Servlet 的話,要加上這部分功能也往往是免費的(或只需要極少的投資)。
與 JSP 比較
JSP 和 Servlet 的區別到底在套用上有哪些體現,很多人搞不清楚。簡單的說,SUN 首先發展出 Servlet,其功能比較強勁,體系設計也很先進,只是,它輸出 HTML 語句還是採用了老的 CGI 方式,是一句一句輸出,所以,編寫和修改 HTML 非常不方便。
Java Server Pages(JSP)是一種實現普通靜態HTML 和動態 HTML 混合編碼的技術,JSP 並沒有增加任何本質上不能用 Servlet 實現的功能。但是,在 JSP 中編寫靜態HTML 更加方便,不必再用 println語 句來輸出每一行 HTML 代碼。更重要的是,藉助內容和外觀的分離,頁面製作中不同性質的任務可以方便地分開:比如,由頁面設計者進行
HTML 設計,同時留出供 Servlet 程式設計師插入動態內容的空間。
後來 SUN 推出了類似於 ASP 的鑲嵌型的 JSP,把 JSP TAG 鑲嵌到 HTML 語句中,這樣,就大大簡化和方便了網頁的設計和修改。新型的網路語言如 ASP,PHP,JSP 都是鑲嵌型的語言。 這是 JSP 和 Servlet 區別的運作原理層面。
從網路三層結構的角度看 JSP 和 Servlet 的區別,一個網路項目最少分三層:data layer(數據層),business layer(業務層),presentation layer(表現層)。當然也可以更複雜。Servlet 用來寫 business layer 是很強大的,但是對於寫 presentation layer 就很不方便。JSP 則主要是為了方便寫 presentation layer 而設計的。當然也可以寫 business layer。寫慣了
ASP ,
PHP ,CGI的朋友,經常會不自覺的把 presentation layer 和 business layer 混在一起。
根據 SUN 自己的推薦,JSP中應該僅僅存放與 presentation layer 有關的東西,也就是說,只放輸出 HTML 網頁的部分。而所有的數據計算,數據分析,資料庫聯結處理,統統是屬於 business layer,應該放在 Java BEANS 中。通過 JSP 調用 Java BEANS,實現兩層的整合。
實際上,微軟前不久推出的 DNA 技術,簡單說,就是 ASP+COM/DCOM 技術。與J SP+BEANS 完全類似,所有的 presentation layer 由 ASP 完成,所有的 business layer 由 COM/DCOM 完成。通過調用,實現整合。
為什麼要採用這些組件技術呢?因為單純的 ASP/JSP 語言是非常低效率執行的,如果出現大量用戶點擊,純 SCRIPT 語言很快就到達了他的功能上限,而組件技術就能大幅度提高功能上限,加快執行速度。
另外一方面,純 SCRIPT 語言將 presentation layer 和 business layer 混在一起,造成修改不方便,並且代碼不能重複利用。如果想修改一個地方,經常會牽涉到十幾頁 code,採用組件技術就只改組件就可以了。
綜上所述,Servlet 是一個早期的不完善的產品,寫 business layer 很好,寫 presentation layer 就很臭,並且兩層混雜。
所以,推出JSP+BEAN,用 JSP 寫 presentation layer,用 BEAN 寫 business layer。SUN 自己的意思也是將來用 JSP 替代 Servlet。這是技術更新方面 JSP 和 Servlet 的區別。
可是,這不是說,學了 Servlet 沒用,實際上,你還是應該從 Servlet 入門,再上 JSP,再上 JSP+BEAN。
強調的是:學了JSP,不會用 Java BEAN 並進行整合,等於沒學。大家多花點力氣在 JSP+BEAN 上。
我們可以看到,當 ASP+COM 和 JSP+BEAN 都採用組件技術後,所有的組件都是先進行編譯,並駐留記憶體,然後快速執行。所以,大家經常吹的 Servlet/JSP 先編譯駐記憶體後執行的速度優勢就沒有了。
反之,ASP+COM+IIS+NT 緊密整合,應該會有較大的速度優勢呈現。而且,ASP+COM+IIS+NT 開發效率非常高,雖然bug 很多。
那么,為什麼還用 JSP+BEAN?因為 Java 實在前途遠大。作業系統群雄並起,套用軟體的開發商必定要找一個通用開發語言進行開發,Java 一統天下的時機就到了。
簡單分析了一下 JSP 和 Servlet 的區別和 Java Web 開發方面的發展。隨著機器速度越來越快,Java 的速度劣勢很快就可以被克服。
版本 版本
日期
JAVA EE/JDK版本
特性
Servlet 4.0
2017年10月
JavaEE 8
HTTP2
Servlet 3.1
2013年5月
JavaEE 7
Non-blocking I/O, HTTP protocol upgrade mechanism
Servlet 3.0
2009年12月
JavaEE 6, JavaSE 6
Pluggability, Ease of development, Async Servlet, Security, File Uploading
Servlet 2.5
2005年10月
JavaEE 5, JavaSE 5
Requires JavaSE 5, supports annotation
Servlet 2.4
2003年11月
J2EE 1.4, J2SE 1.3
web.xml uses XML Schema
Servlet 2.3
2001年8月
J2EE 1.3, J2SE 1.2
Addition of Filter
Servlet 2.2
1999年8月
J2EE 1.2, J2SE 1.2
Becomes part of J2EE, introduced independent web applications in .war files
Servlet 2.1
1998年11月
未指定
First official specification, added RequestDispatcher, ServletContext
Servlet 2.0
JDK 1.1
Part of Java Servlet Development Kit 2.0
Servlet 1.0
1997年6月
新增功能 Servlet 2.2
:引入了 self-contained Web applications 的概念。
servlet 2.3
: 2000年10月份出來
Servlet API 2.3中最重大的改變是增加了 filters
Servlet 2.3 增加了 filters 和 filter chains 的功能。引入了 context 和 session listeners 的概念,當 context 或 session 被初始化或者被將要被釋放的時候,和當向 context 或 session 中綁定屬性或解除綁定的時候,可以對類進行監測。
servlet 2.4
: 2003年11月份推出
Servlet 2.4 加入了幾個引起關注的特性,沒有特別突出的新內容,而是花費了更多的功夫在推敲和闡明以前存在的一些特性上,對一些不嚴謹的地方進行了校驗。
Servlet 2.4 增加了新的最低需求,新的監測 request 的方法,新的處理 response 的方法,新的國際化支持,RequestDispatcher 的幾個處理,新的 request listener 類,session 的描述,和一個新的基於 Schema 的並擁有 J2EE 元素的發布描述符。這份文檔規範全面而嚴格的進行了修訂,除去了一些可能會影響到跨平台發布的模糊不清的因素。總而言之,這份規範增加了四個新類,七個新方法,一個新常量,不再推薦使用一個類。
注意:改為 Schema 後主要加強了兩項功能:
(1) 元素不依照順序設定;
(2) 更強大的驗證機制。
主要體現在:
a.檢查元素的值是否為合法的值
b.檢查元素的值是否為合法的文字字元或者數字字元
c.檢查 Servlet, Filter, EJB-ref 等等元素的名稱是否唯一
2.新增 Filter 四種設定:REQUEST、FORWARD、INCLUDE 和 ERROR。
3.新增 Request Listener、Event和Request Attribute Listener、Event。
4.取消 SingleThreadModel 接口。當 Servlet 實現 SingleThreadModel 接口時,它能確保同時間內,只能有一個 thread 執行此 Servlet。
5.<welcome-file-list>可以為Servlet。
6.ServletRequest接口新增一些方法。
public String getLocalName();
public String getLocalAddr();
public int getLocalPort();
public int getRemotePort()
Servlet 2.5
2005 年 9 月發布 Servlet 2.5
Servlet 2.5 一些變化的介紹:
1) 基於最新的 J2SE 5.0 開發的。
2) 支持 annotations 。
3) web.xml 中的幾處配置更加方便。
4) 去除了少數的限制。
5) 最佳化了一些實例
Servlet 的各個版本對監聽器的變化有:
(1) Servlet 2.2 和 jsp1.1
新增Listener:HttpSessionBindingListener
新增Event: HttpSessionBindingEvent
(2) Servlet 2.3 和 jsp1.2
新增Listener:ServletContextListener,ServletContextAttributeListener
,HttpSessionListener,HttpSessionActivationListener,HttpSessionAttributeListener
新增Event: ServletContextEvent,ServletContextAttributeEvent,HttpSessionEvent
(3) Servlet 2.4 和 jsp2.0
新增Listener:ServletRequestListener,ServletRequestAttribureListener
新增Event: ServletRequestEvent,ServletRequestAttributeEvent
Servlet 3.0
Servlet 3.0 作為 Java EE 6 規範體系中一員,隨著 Java EE 6 規範一起發布。該版本在前一版本(Servlet 2.5)的基礎上提供了若干新特性用於簡化 Web 套用的開發和部署。其中有幾項特性的引入讓開發者感到非常興奮,同時也獲得了 Java 社區的一片讚譽之聲:
異步處理支持:有了該特性,Servlet 執行緒不再需要一直阻塞,直到業務處理完畢才能再輸出回響,最後才結束該 Servlet 執行緒。在接收到請求之後,Servlet 執行緒可以將耗時的操作委派給另一個執行緒來完成,自己在不生成回響的情況下返回至容器。針對業務處理較耗時的情況,這將大大減少伺服器資源的占用,並且提高並發處理速度。
新增的註解支持:該版本新增了若干註解,用於簡化 Servlet、過濾器(Filter)和監聽器(Listener)的聲明,這使得 web.xml 部署描述檔案從該版本開始不再是必選的了。
可插性支持:熟悉 Struts2 的開發者一定會對其通過外掛程式的方式與包括 Spring 在內的各種常用框架的整合特性記憶猶新。將相應的外掛程式封裝成 JAR 包並放在類路徑下,Struts2 運行時便能自動載入這些外掛程式。現在 Servlet 3.0 提供了類似的特性,開發者可以通過外掛程式的方式很方便的擴充已有 Web 套用的功能,而不需要修改原有的套用。
Servlet 4.0
從3.1到4.0將是對Servlet 協定的一次大改動,而改動的關鍵之處在於對HTTP/2的支持。HTTP2將是是繼上世紀末HTTP1.1協定規範化以來首個HTTP協定新版本,相對於HTTP1.1,HTTP2將帶來許多的增強。在草案提議中,Shing Wai列舉出了一些HTTP2的新特性,而這些特性也正是他希望在Servlet 4.0 API中實現並暴露給用戶的新功能,這些新特性如下:
1.請求/回響復用(Request/Response multiplexing) 2.流的優先權(Stream Prioritization) 3.伺服器推送(Server Push) 4.HTTP1.1升級(Upgrade from HTTP 1.1)
規範 1.簡化開發
2.便於部署
3.支持 Web2.0 原則
為了簡化開發流程,Servlet 3.0 引入了註解(annotation),這使得 web 部署描述符 web.xml 不再是必須的選擇。
Pluggability 可插入性
當使用任何第三方的框架,如 Struts,JSF 或 Spring,我們都需要在 web.xml 中添加對應的 Servlet 的入口。這使得 web 描述符笨重而難以維護。Servlet3.0 的新的可插入特性使得 web 應用程式模組化而易於維護。通過 web fragment 實現的可插入性減輕了開發人員的負擔,不需要再在 web.xml 中配置很多的 Servlet 入口。
Asynchronous Processing 異步處理
另外一個顯著的改變就是 Servlet 3.0 支持異步處理,這對 AJAX 應用程式非常有用。當一個 Servlet 創建一個執行緒來處理某些請求的時候,如查詢資料庫或訊息連線,這個執行緒要等待直到獲得所需要的資源才能夠執行其他的操作。異步處理通過運行執行緒執行其他的操作來避免了這種阻塞。
Apart from the features mentioned here, several other enhancements have been made to the existing API. The sections towards the end of the article will explore these features one by one in detail.
除了這些新特性之外, Servlet 3.0對已有的 API 也做了一些改進,在本文的最後我們會做介紹。
Annotations in Servlet Servlet 中使用註解
Servlet 3.0 的一個主要的改變就是支持註解。使用註解來定義 Servlet 和 filter 使得我們不用在 web.xml 中定義相應的入口。
@WebServlet
@WebServlet 用來定義 web 應用程式中的一個 Servlet。這個註解可以套用於繼承了 HttpServlet。這個註解有多個屬性,例如 name,urlPattern, initParams,我們可以使用者的屬性來定義 Servlet 的行為。urlPattern 屬性是必須指定的。
編程接口 HTTPServlet 使用一個 HTML 表單來傳送和接收數據。要創建一個 HTTPServlet,請擴展
HttpServlet 類, 該類是用專門的方法來處理 HTML
表單 的 GenericServlet 的一個子類。 HTML 表單是由 <form> 和 </form> 標記定義的。表單中典型地包含輸入欄位(如文本輸入欄位、
複選框 、
單選按鈕 和選擇列表)和用於提交數據的按鈕。當提交信息時,它們還指定
伺服器 應執行哪一個Servlet(或其它的程式)。 HttpServlet 類包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是
繼承 的。
(1) init() 方法
在 Servlet 的生命期中,僅執行一次 init() 方法。它是在伺服器裝入 Servlet 時執行的。 可以
配置伺服器 ,以在啟動伺服器或客戶機首次訪問 Servlet 時裝入 Servlet。 無論有多少客戶機訪問 Servlet,都不會重複執行 init() 。
預設的 init() 方法通常是符合要求的,但也可以用定製 init() 方法來覆蓋它,典型的是管理
伺服器 端資源。 例如,可能編寫一個定製 init() 來只用於一次裝入 GIF 圖像,改進 Servlet 返回 GIF 圖像和含有多個客戶機請求的性能。另一個示例是初始化資料庫連線。預設的 init() 方法設定了 Servlet 的初始化參數,並用它的 ServletConfig 對象參數來啟動配置, 因此所有覆蓋 init() 方法的 Servlet 應調用 super.init() 以確保仍然執行這些任務。在調用 service() 方法之前,應確保已完成了 init() 方法。
(2) service() 方法
service() 方法是 Servlet 的核心。每當一個客戶請求一個HttpServlet 對象,該對象的service() 方法就要被調用,而且傳遞給這個方法一個"請求"(ServletRequest)對象和一個"回響"(ServletResponse)對象作為參數。 在 HttpServlet 中已存在 service() 方法。預設的服務功能是調用與 HTTP 請求的方法相應的 do 功能。例如, 如果 HTTP 請求方法為 GET,則預設情況下就調用 doGet() 。Servlet 應該為 Servlet 支持的 HTTP 方法覆蓋 do 功能。因為 HttpServlet.service() 方法會檢查請求方法是否調用了適當的處理方法,不必要覆蓋 service() 方法。只需覆蓋相應的 do 方法就可以了。
Servlet 的回響可以是下列幾種類型:
一個輸出流,瀏覽器根據它的內容類型(如 text/html)進行解釋。
一個 HTTP 錯誤回響,重定向到另一個 URL、servlet、JSP。
(3) doGet() 方法
當一個客戶通過 HTML
表單 發出一個 HTTP GET 請求或直接請求一個 URL 時,doGet() 方法被調用。與 GET 請求相關的參數添加到 URL 的後面,並與這個請求一起傳送。當不會修改
伺服器 端的數據時,應該使用 doGet() 方法。
(4) doPost() 方法
當一個客戶通過 HTML 表單發出一個 HTTP POST 請求時,doPost() 方法被調用。與 POST 請求相關的參數作為一個單獨的 HTTP 請求從瀏覽器傳送到伺服器。當需要修改伺服器端的數據時,應該使用 doPost() 方法。
(5) destroy() 方法
destroy() 方法僅執行一次,即在伺服器停止且卸裝 Servlet 時執行該方法。典型的,將 Servlet 作為伺服器進程的一部分來關閉。預設的 destroy() 方法通常是符合要求的,但也可以覆蓋它,典型的是管理伺服器端資源。例如,如果 Servlet 在運行時會累計統計數據,則可以編寫一個 destroy() 方法,該方法用於在未裝入 Servlet 時將統計數字保存在檔案中。另一個示例是關閉資料庫連線。
當
伺服器 卸裝 Servlet 時,將在所有 service() 方法調用完成後,或在指定的時間間隔過後調用 destroy() 方法。一個 Servlet 在運行 service() 方法時可能會產生其它的執行緒,因此請確認在調用 destroy() 方法時,這些執行緒已終止或完成。
(6) getServletConfig() 方法
getServletConfig() 方法返回一個 ServletConfig 對象,該對象用來返回初始化參數和 ServletContext。ServletContext 接口提供有關 servlet 的環境信息。
(7) getServletInfo() 方法
getServletInfo() 方法是一個可選的方法,它提供有關 servlet 的信息,如作者、版本、著作權。
當
伺服器 調用 sevlet 的 service()、doGet() 和 doPost() 這三個方法時,均需要 “請求”和“回響”對象作為參數。“請求”對象提供有關請求的信息,而“回響”對象提供了一個將回響信息返回給瀏覽器的一個通信途徑。
javax.servlet 軟體包中的相關類為 ServletResponse 和 ServletRequest,而 javax.servlet.
http 軟體包中的相關類為 HttpServletRequest 和 HttpServletResponse。Servlet 通過這些對象與
伺服器 通信並最終與客戶端通信。Servlet 能通過調用"請求"對象的方法獲知客戶端環境,伺服器環境的信息和所有由客戶機提供的信息。Servlet 可以調用“回響”對象的方法傳送回響,該回響是準備發回客戶端的。
常見容器 Tomcat, Jetty, resin, Oracle Application server, WebLogic Server, Glassfish, Websphere, JBoss 等等。(提供了 Servlet 功能的伺服器,叫做 Servlet 容器。對 web 程式來說,Servlet 容器的作用就相當於桌面程式里作業系統的作用,都是提供一些編程基礎設施)
建議 在 Web 應用程式中,一個 Servlet 在一個時刻可能被多個用戶同時訪問。這時 Web 容器將為每個用戶創建一個執行緒來執行 Servlet。如果 Servlet 不涉及共享資源的問題,不必關心多執行緒問題。但如果 Servlet 需要共享資源,需要保證 Servlet 是
執行緒安全 的。
下面是編寫執行緒安全的 Servlet 的一些建議:
(1)用方法的局部變數保存請求中的專有數據。對方法中定義的局部變數,進入方法的每個執行緒都有自己的一份方法變數拷貝。任何執行緒都不會修改其他執行緒的局部變數。如果要在不同的請求之間共享數據,應該使用會話來共享這類數據。
(2)只用 Servlet的成員變數來存放那些不會改變的數據。有些數據在 Servlet 生命周期中不發生任何變化,通常是在初始時確定的,這些數據可以使用成員變數保存,如資料庫連線名稱、其他資源的路徑等。
(3)對可能被請求修改的成員變數同步。有時數據成員變數或者環境屬性可能被請求修改。當訪問這些數據時應該對它們同步,以避免多個執行緒同時修改這些數據。
(4)如果 Servlet 訪問外部資源,那么需要同步訪問這些資源。例如,假設 Servlet 要從檔案中讀寫數據。當一個執行緒讀寫一個檔案時,其他執行緒也可能正在讀寫這個檔案。檔案訪問本身不是執行緒安全的,所以必須編寫同步訪問這些資源的代碼。在編寫執行緒安全的 Servlet 時,下面兩種方法是不應該使用的:
(1)在 Servlet API 中提供了一個 SingleThreadModel 接口,實現這個接口的 Servlet 在被多個客戶請求時一個時刻只有一個執行緒運行。這個接口已被標記不推薦使用。
(2)對 doGet() 或doPost() 方法同步。如果必須在 Servlet 中使用同步代碼,應儘量在最小的代碼塊範圍上進行同步。同步代碼越小,Servlet 執行得才越好。