Servlet

Servlet

Servlet是用Java編寫的Server端程式,它與協定和平台無關。Servlet運行於Java-enabled Web Server中。Java Servlet可以動態地擴展Server的能力,並採用請求-回響模式提供Web服務。最早支持Servlet技術的是JavaSoft的Java Web Server。此後,一些其它的基於Java的Web Server開始支持標準的Servlet API。

基本信息

由來

ServletServlet
Servlet是在伺服器上運行的小程式。這個詞是在Javaapplet的環境中創造的,Javaapplet是一種當作單獨檔案跟網頁一起傳送的小程式,它通常用於在客戶端運行,結果得到為用戶進行運算或者根據用戶互作用定點陣圖形等服務。
伺服器上需要一些程式,常常是根據用戶輸入訪問資料庫的程式。這些通常是使用公共網關接口(CommonGatewayInterface,CGI)應用程式完成的。然而,在伺服器上運行Java,這種程式可使用Java程式語言實現。在通信量大的伺服器上,JavaServlet的優點在於它們的執行速度更快於CGI程式。各個用戶請求被激活成單個程式中的一個執行緒,而無需創建單獨的進程,這意味著伺服器端處理請求的系統開銷將明顯降低。
實現過程
最早支持Servlet技術的是JavaSoft的JavaWebServer。此後,一些其它的基於Java的WebServer開始支持標準的ServletAPI。Servlet的主要功能在於互動式地瀏覽和修改數據,生成動態Web內容。這個過程為:
1)客戶端傳送請求至伺服器端;
2)伺服器將請求信息傳送至Servlet;
3)Servlet生成回響內容並將其傳給伺服器。回響內容動態生成,通常取決於客戶端的請求;
4)伺服器將回響返回給客戶端。
Servlet看起來像是通常的Java程式。Servlet導入特定的屬於JavaServletAPI的包。因為是對象位元組碼,可動態地從網路載入,可以說Servlet對Server就如同Applet對Client一樣,但是,由於Servlet運行於Server中,它們並不需要一個圖形用戶界面。從這個角度講,Servlet也被稱為FacelessObject。
一個Servlet就是Java程式語言中的一個類,它被用來擴展伺服器的性能,伺服器上駐留著可以通過“請求-回響”編程模型來訪問的應用程式。雖然Servlet可以對任何類型的請求產生回響,但通常只用來擴展Web伺服器的應用程式。
目前最新版本為3.1。

命名

Servlet的命名可以看出sun命名的特點,如Applet表示小應用程式;Scriptlet=Script+Applet,表示小腳本程式;同樣Servlet=Server+Applet,表示小服務程式。

生命周期

ServletServlet
客戶端請求該Servlet;
載入Servlet類到記憶體;
實例化並調用init()方法初始化該Servlet;
service()(根據請求方法不同調用doGet()或者doPost(),此外還有doGet()、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根據客戶端請求生成回響內容並將其傳給伺服器
伺服器將回響返回客戶端

比較

與Applet的比較
相似之處:
*它們不是獨立的應用程式,沒有main()方法。
*它們不是由用戶或程式設計師調用,而是由另外一個應用程式(容器)調用。
*它們都有一個生存周期,包含init()和destroy()方法。
不同之處:
*Applet具有很好的圖形界面(AWT),與瀏覽器一起,在客戶端運行。
*Servlet則沒有圖形界面,運行在伺服器端。
與CGI比較
與傳統的CGI和許多其他類似CGI的技術相比,JavaServlet具有更高的效率,更容易使用,功能更強大,具有更好的可移植性,更節省投資。在未來的技術發展過程中,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編寫,ServletAPI具有完善的標準。因此,為IPlanetEnterpriseServer寫的Servlet無需任何實質上的改動即可移植到Apache、MicrosoftIIS或者WebStar。幾乎所有的主流伺服器都直接或通過外掛程式支持Servlet。
*節省投資
不僅有許多廉價甚至免費的Web伺服器可供個人或小規模網站使用,而且對於現有的伺服器,如果它不支持Servlet的話,要加上這部分功能也往往是免費的(或只需要極少的投資)。
與JSP比較
JSP和Servlet的區別到底在套用上有哪些體現,很多人搞不清楚。簡單的說,SUN首先發展出Servlet,其功能比較強勁,體系設計也很先進,只是,它輸出HTML語句還是採用了老的CGI方式,是一句一句輸出,所以,編寫和修改HTML非常不方便。
JavaServerPages(JSP)是一種實現普通靜態HTML和動態HTML混合編碼的技術,JSP並沒有增加任何本質上不能用Servlet實現的功能。但是,在JSP中編寫靜態HTML更加方便,不必再用println語句來輸出每一行HTML代碼。更重要的是,藉助內容和外觀的分離,頁面製作中不同性質的任務可以方便地分開:比如,由頁面設計者進行HTML設計,同時留出供Servlet程式設計師插入動態內容的空間。
後來SUN推出了類似於ASP的鑲嵌型的JSP,把JSPTAG鑲嵌到HTML語句中,這樣,就大大簡化和方便了網頁的設計和修改。新型的網路語言如ASP,PHP,JSP都是鑲嵌型的語言。這是JSP和Servlet區別的運作原理層面。
從網路三層結構的角度看JSP和Servlet的區別,一個網路項目最少分三層:datalayer(數據層),businesslayer(業務層),presentationlayer(表現層)。當然也可以更複雜。Servlet用來寫businesslayer是很強大的,但是對於寫presentationlayer就很不方便。JSP則主要是為了方便寫presentationlayer而設計的。當然也可以寫businesslayer。寫慣了ASP,PHP,CGI的朋友,經常會不自覺的把presentationlayer和businesslayer混在一起。
根據SUN自己的推薦,JSP中應該僅僅存放與presentationlayer有關的東西,也就是說,只放輸出HTML網頁的部分。而所有的數據計算,數據分析,資料庫聯結處理,統統是屬於businesslayer,應該放在JavaBEANS中。通過JSP調用JavaBEANS,實現兩層的整合。
實際上,微軟前不久推出的DNA技術,簡單說,就是ASP+COM/DCOM技術。與JSP+BEANS完全類似,所有的presentationlayer由ASP完成,所有的businesslayer由COM/DCOM完成。通過調用,實現整合。
為什麼要採用這些組件技術呢?因為單純的ASP/JSP語言是非常低效率執行的,如果出現大量用戶點擊,純SCRIPT語言很快就到達了他的功能上限,而組件技術就能大幅度提高功能上限,加快執行速度。
另外一方面,純SCRIPT語言將presentationlayer和businesslayer混在一起,造成修改不方便,並且代碼不能重複利用。如果想修改一個地方,經常會牽涉到十幾頁code,採用組件技術就只改組件就可以了。
綜上所述,Servlet是一個早期的不完善的產品,寫businesslayer很好,寫presentationlayer就很臭,並且兩層混雜。
所以,推出JSP+BEAN,用JSP寫presentationlayer,用BEAN寫businesslayer。SUN自己的意思也是將來用JSP替代Servlet。這是技術更新方面JSP和Servlet的區別。
可是,這不是說,學了Servlet沒用,實際上,你還是應該從Servlet入門,再上JSP,再上JSP+BEAN。
強調的是:學了JSP,不會用JavaBEAN並進行整合,等於沒學。大家多花點力氣在JSP+BEAN上。
我們可以看到,當ASP+COM和JSP+BEAN都採用組件技術後,所有的組件都是先進行編譯,並駐留記憶體,然後快速執行。所以,大家經常吹的Servlet/JSP先編譯駐記憶體後執行的速度優勢就沒有了。
反之,ASP+COM+IIS+NT緊密整合,應該會有較大的速度優勢呈現。而且,ASP+COM+IIS+NT開發效率非常高,雖然bug很多。
那么,為什麼還用JSP+BEAN?因為Java實在前途遠大。微軟分拆後,作業系統將群雄並起,套用軟體的開發商必定要找一個通用開發語言進行開發,Java一統天下的時機就到了。如果微軟分拆順利,從中分出的套用軟體公司將成為Java的新領導者。目前的Java大頭SUN和IBM都死氣沉沉,令人失望。希望新公司能注入新活力。不過,新公司很有可能和舊SUN展開Java標準大戰,雙方各自製定標準,影響Java跨平台。
簡單分析了一下JSP和Servlet的區別和JavaWeb開發方面的發展。隨著機器速度越來越快,Java的速度劣勢很快就可以被克服。

新增功能

Servlet2.2

引入了self-containedWebapplications的概念。

servlet2.3

2000年10月份出來
ServletAPI2.3中最重大的改變是增加了filters
Servlet2.3增加了filters和filterchains的功能。引入了context和sessionlisteners的概念,當context或session被初始化或者被將要被釋放的時候,和當向context或session中綁定屬性或解除綁定的時候,可以對類進行監測。

servlet2.4

2003年11月份出來
Servlet2.4加入了幾個引起關注的特性,沒有特別突出的新內容,而是花費了更多的功夫在推敲和闡明以前存在的一些特性上,對一些不嚴謹的地方進行了校驗。
Servlet2.4增加了新的最低需求,新的監測request的方法,新的處理response的方法,新的國際化支持,RequestDispatcher的幾個處理,新的requestlistener類,session的描述,和一個新的基於Schema的並擁有J2EE元素的發布描述符。這份文檔規範全面而嚴格的進行了修訂,除去了一些可能會影響到跨平台發布的模糊不清的因素。總而言之,這份規範增加了四個新類,七個新方法,一個新常量,不再推薦使用一個類。
注意:改為Schema後主要加強了兩項功能:
(1)元素不依照順序設定;
(2)更強大的驗證機制。
主要體現在:
a.檢查元素的值是否為合法的值
b.檢查元素的值是否為合法的文字字元或者數字字元
c.檢查Servlet,Filter,EJB-ref等等元素的名稱是否唯一
2.新增Filter四種設定:REQUEST、FORWARD、INCLUDE和ERROR。
3.新增RequestListener、Event和RequestAttributeListener、Event。
4.取消SingleThreadModel接口。當Servlet實現SingleThreadModel接口時,它能確保同時間內,只能有一個thread執行此Servlet。
5.<welcome-file-list>可以為Servlet。
6.ServletRequest接口新增一些方法。
publicStringgetLocalName();
publicStringgetLocalAddr();
publicintgetLocalPort();
publicintgetRemotePort()

Servlet2.5

2005年9月發布Servlet2.5
Servlet2.5一些變化的介紹:
1)基於最新的J2SE5.0開發的。
2)支持annotations。
3)web.xml中的幾處配置更加方便。
4)去除了少數的限制。
5)最佳化了一些實例
Servlet的各個版本對監聽器的變化有:
(1)Servlet2.2和jsp1.1
新增Listener:HttpSessionBindingListener
新增Event:HttpSessionBindingEvent
(2)Servlet2.3和jsp1.2
新增Listener:ServletContextListener,ServletContextAttributeListener
,HttpSessionListener,HttpSessionActivationListener,HttpSessionAttributeListener
新增Event:ServletContextEvent,ServletContextAttributeEvent,HttpSessionEvent
(3)Servlet2.4和jsp2.0
新增Listener:ServletRequestListener,ServletRequestAttribureListener
新增Event:ServletRequestEvent,ServletRequestAttributeEvent

Servlet3.0

Servlet3.0作為JavaEE6規範體系中一員,隨著JavaEE6規範一起發布。該版本在前一版本(Servlet2.5)的基礎上提供了若干新特性用於簡化Web套用的開發和部署。其中有幾項特性的引入讓開發者感到非常興奮,同時也獲得了Java社區的一片讚譽之聲:
異步處理支持:有了該特性,Servlet執行緒不再需要一直阻塞,直到業務處理完畢才能再輸出回響,最後才結束該Servlet執行緒。在接收到請求之後,Servlet執行緒可以將耗時的操作委派給另一個執行緒來完成,自己在不生成回響的情況下返回至容器。針對業務處理較耗時的情況,這將大大減少伺服器資源的占用,並且提高並發處理速度。
新增的註解支持:該版本新增了若干註解,用於簡化Servlet、過濾器(Filter)和監聽器(Listener)的聲明,這使得web.xml部署描述檔案從該版本開始不再是必選的了。
可插性支持:熟悉Struts2的開發者一定會對其通過外掛程式的方式與包括Spring在內的各種常用框架的整合特性記憶猶新。將相應的外掛程式封裝成JAR包並放在類路徑下,Struts2運行時便能自動載入這些外掛程式。現在Servlet3.0提供了類似的特性,開發者可以通過外掛程式的方式很方便的擴充已有Web套用的功能,而不需要修改原有的套用。

Servlet4.0草案

從3.1到4.0將是對Servlet協定的一次大改動,而改動的關鍵之處在於對HTTP/2的支持。HTTP2將是是繼上世紀末HTTP1.1協定規範化以來首個HTTP協定新版本,相對於HTTP1.1,HTTP2將帶來許多的增強。在草案提議中,ShingWai列舉出了一些HTTP2的新特性,而這些特性也正是他希望在Servlet4.0API中實現並暴露給用戶的新功能,這些新特性如下:
1.請求/回響復用(Request/Responsemultiplexing)
2.流的優先權(StreamPrioritization)
3.伺服器推送(ServerPush)
4.HTTP1.1升級(UpgradefromHTTP1.1)

規範

1.簡化開發
2.便於部署
3.支持Web2.0原則
為了簡化開發流程,Servlet3.0引入了註解(annotation),這使得web部署描述符web.xml不再是必須的選擇。
Pluggability可插入性
當使用任何第三方的框架,如Struts,JSF或Spring,我們都需要在web.xml中添加對應的Servlet的入口。這使得web描述符笨重而難以維護。Servlet3.0的新的可插入特性使得web應用程式模組化而易於維護。通過webfragment實現的可插入性減輕了開發人員的負擔,不需要再在web.xml中配置很多的Servlet入口。
AsynchronousProcessing異步處理
另外一個顯著的改變就是Servlet3.0支持異步處理,這對AJAX應用程式非常有用。當一個Servlet創建一個執行緒來處理某些請求的時候,如查詢資料庫或訊息連線,這個執行緒要等待直到獲得所需要的資源才能夠執行其他的操作。異步處理通過運行執行緒執行其他的操作來避免了這種阻塞。
Apartfromthefeaturesmentionedhere,severalotherenhancementshavebeenmadetotheexistingAPI.Thesectionstowardstheendofthearticlewillexplorethesefeaturesonebyoneindetail.
除了這些新特性之外,Servlet3.0對已有的API也做了一些改進,在本文的最後我們會做介紹。
AnnotationsinServletServlet中使用註解
Servlet3.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表單發出一個HTTPGET請求或直接請求一個URL時,doGet()方法被調用。與GET請求相關的參數添加到URL的後面,並與這個請求一起傳送。當不會修改伺服器端的數據時,應該使用doGet()方法。
(4)doPost()方法
當一個客戶通過HTML表單發出一個HTTPPOST請求時,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,OracleApplicationserver,WebLogicServer,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)在ServletAPI中提供了一個SingleThreadModel接口,實現這個接口的Servlet在被多個客戶請求時一個時刻只有一個執行緒運行。這個接口已被標記不推薦使用。
(2)對doGet()或doPost()方法同步。如果必須在Servlet中使用同步代碼,應儘量在最小的代碼塊範圍上進行同步。同步代碼越小,Servlet執行得才越好。

相關詞條

相關搜尋

熱門詞條

聯絡我們