鍍金池/ 教程/ Java/ Servlet 與 JSP
Struts2
Java 泛型
排序算法
Java 內(nèi)存管理
Webservice
Spring
輸入輸出流
Socket
字符串與數(shù)組
面向?qū)ο缶幊?/span>
海量數(shù)據(jù)處理
Hibernate
Netty
基本類型與運(yùn)算符
常見設(shè)計(jì)模式
Java 虛擬機(jī)
Java 多線程
JDBC
搭建 Java 開發(fā)環(huán)境
Java 數(shù)據(jù)庫操作
異常處理
集合類
Servlet 與 JSP

Servlet 與 JSP

JSP 與 SERVLET 的關(guān)系

綜述:Java Servlet 是 JSP 技術(shù)的基礎(chǔ),而且大型的 Web 應(yīng)用程序的開發(fā)需要 Java Servlet 和 JSP 配合才能完成?,F(xiàn)在許多 Web 服務(wù)器都支持 Servlet,即使不直接支持 Servlet 的 Web 服務(wù)器,也可以通過附件的應(yīng)用服務(wù)器和模塊來支持 Servlet,這得益于 Java 的跨平臺(tái)特性。另外,由于 Servlet 內(nèi)部以線程方式提供提供服務(wù),不必對(duì)于每個(gè)請(qǐng)求都啟動(dòng)一個(gè)進(jìn)程,并且利用多線程機(jī)制可以同時(shí)為多個(gè)請(qǐng)求服務(wù),因此 Servlet 的效率非常高。

但它并不是沒有缺點(diǎn),和傳統(tǒng)的 CGI、ISAPI、NSAPI 方式相同,Java Servlet 也是利用輸出 HTML 語句來實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)頁的,如果用它來開發(fā)整個(gè)網(wǎng)站,動(dòng)態(tài)部分和靜態(tài)頁面的整合過程將變得無法想象。這就是 SUN 還要推出 JSP 的原因。

如何正確理解 servlet?

servlet 的基本概念

一、Servlet 的結(jié)構(gòu)

在具體掌握 servlet 之前,須對(duì) Java 語言有所了解。我們假設(shè)讀者已經(jīng)具備一定的 Java 基礎(chǔ)。在 Servlet API 中最重要的是 Servlet 接口(interface),所有的 servlets 都必須實(shí)現(xiàn)該接口,途徑有很多:一是直接實(shí)現(xiàn)該接口,二是通過擴(kuò)展類(class)來實(shí)現(xiàn),如 HttpServlet。 這個(gè) Servlet 接口提供了 servlet 與客戶端聯(lián)系的方法。Servlet 編寫者可以在他們開發(fā) servlet 程序時(shí)提供更多一些或所有的這樣方法。

當(dāng)一個(gè) servlet 接收來自客戶端的調(diào)用請(qǐng)求, 它接收兩個(gè)對(duì)象:一個(gè)是 ServletRequest,另外一個(gè)是 ServletResponse。這個(gè) ServletRequest 類概括從客戶端到服務(wù)器之間的聯(lián)系,而 ServletResponse 類概括從 servlet 返回客戶端的聯(lián)系。

ServletRequest 接口可以獲取到這樣一些信息,如由客戶端傳送的闡述名稱,客戶端正在使用的協(xié)議,產(chǎn)生請(qǐng)求并且接收請(qǐng)求的服務(wù)器遠(yuǎn)端主機(jī)名。它也提供獲取數(shù)據(jù)流的 ServletInputStream, 這些數(shù)據(jù)是客戶端引用中使用 HTTP POST 和 PUT 方法遞交的。一個(gè) ServletRequest 的子類可以讓 servlet 獲取更多的協(xié)議特性數(shù)據(jù)。例如: HttpServletRequest 包含獲取 HTTP-specific 頭部信息的方法。

ServletResponse 接口給出相應(yīng)客戶端的 servlet 方法。它允許 servlet 設(shè)置內(nèi)容長(zhǎng)度和回應(yīng)的 mime 類型,并且提供輸出流 ServletOutputStream,通過編寫者可以發(fā)回相應(yīng)的數(shù)據(jù)。 ServletResponse 子類可以給出更多 protocol- specific 內(nèi)容的信息。 例如:HttpServletResponse 包含允許 servlet 操作 HTTP-specific 頭部信息的方法。

上面有關(guān)類和接口的描述,構(gòu)成了一個(gè)基本的 Servlet 框架。HTTP servlets 有一些附加的可以提供 session-tracking capabilities 的方法。servlet 編寫者可以利用這些 API,在有他人操作時(shí)維護(hù) servlet 與客戶端之間的狀態(tài)。

二、Servlet 的接口

我們編寫的 Servlet ,一般從 Javax 包的 HttpServlet 類擴(kuò)展而來,在 HttpServlet 中加入了一些附加的方法,這些方法可以被協(xié)助處理 HTTP 基本請(qǐng)求的 HttpServlet 類中的方法 service 自動(dòng)地調(diào)用。這些方法有:

· doGet 用來處理 HTTP 的 GET 請(qǐng)求。

這個(gè) GET 操作僅僅允許客戶從 HTTP server 上取得(GET)資源。重載此方法的用戶自動(dòng)允許支持方法 HEAD。這個(gè) GET 操作被認(rèn)為是安全的,沒有任何的負(fù)面影響,對(duì)用戶來說是很可靠的。比如,大多數(shù)的正規(guī)查詢都沒有副作用。打算改變存儲(chǔ)數(shù)據(jù)的請(qǐng)求必須用其他的 HTTP 方法。這些方法也必須是個(gè)安全的操作。方法 doGet 的缺省實(shí)現(xiàn)將返回一個(gè) HTTP 的 BAD_REQUEST 錯(cuò)誤。

方法 doGet 的格式:

protected void doGet(HttpServletResquest request, HttpServletResponse response)
throws ServletException,IOException;

· doPost 用來處理 HTTP 的 POST 請(qǐng)求。

這個(gè) POST 操作包含了在必須通過此 servlet 執(zhí)行的請(qǐng)求中的數(shù)據(jù)。由于它不能立即取得資源,故對(duì)于那些涉及到安全性的用戶來說,通過 POST 請(qǐng)求操作會(huì)有一些副作用。

方法 doPost 的缺省實(shí)現(xiàn)將返回一個(gè) HTTP 的 BAD_REQUEST 錯(cuò)誤。當(dāng)編寫 servlet 時(shí),為了支持 POST 操作必須在子類 HttpServlet 中實(shí)現(xiàn)(implement)此方法。

此方法的格式:

protected void doPost(HttpServletResquest request, HttpServletResponse response)
throws ServletException,IOException;

· doPut 用來處理 HTTP 的 PUT 請(qǐng)求。

此 PUT 操作模擬通過 FTP 發(fā)送一個(gè)文件。對(duì)于那些涉及到安全性的用戶來說,通過 PUT 請(qǐng)求操作也會(huì)有一些副作用。

此方法的格式:

protected void doPut(HttpServletResquest request,HttpServletResponse response)
throws ServletException,IOException;

· doDelete 用來處理 HTTP 的 DELETE 請(qǐng)求。

此操作允許客戶端請(qǐng)求一個(gè)從 server 移出的 URL。對(duì)于那些涉及到安全性的用戶來說,通過 DELETE 請(qǐng)求操作會(huì)有一些副作用。

方法 doDelete 的缺省實(shí)現(xiàn)將返回一個(gè) HTTP 的 BAD_REQUEST 錯(cuò)誤。當(dāng)編寫 servlet 時(shí),為了支持 DELETE 操作,必須在子類 HttpServlet 中實(shí)現(xiàn)(implement)此方法。

此方法的格式:

protected void doDelete (HttpServletResquest request, HttpServletResponse response) 
throws ServletException,IOException;

· doHead 用來處理 HTTP 的 HEAD 請(qǐng)求。

缺省地,它會(huì)在無條件的 GET 方法執(zhí)行時(shí)運(yùn)行,但是不返回任何數(shù)據(jù)到客戶端。只返回包含內(nèi)容信息的長(zhǎng)度的 header。由于用到 GET 操作,此方法應(yīng)該是很安全的(沒有副作用)也是可重復(fù)使用的。此方法的缺省實(shí)現(xiàn)(implement)自動(dòng)地處理了 HTTPDE 的 HEAD 操作并且不需要通過一個(gè)子類實(shí)現(xiàn)(implement)。

此方法的格式:

protected void doHead (HttpServletResquest request,HttpServletResponse response) 
throws ServletException,IOException;

· doOptions 用來處理 HTTP 的 OPTIONS 請(qǐng)求。

此操作自動(dòng)地決定支持什么 HTTP 方法。比如說,如果讀者創(chuàng)建 HttpServlet 的子類并重載方法 doGet,然后方法 doOptions 會(huì)返回下面的 header:

Allow:GET,HEAD,TRACE,OPTIONS

一般不需要重載方法 doOptions。

此方法的格式:

protected void doOptions (HttpServletResquest request, HttpServletResponse response)
throws ServletException,IOException;

· doTrace 用來處理 HTTP 的 TRACE 請(qǐng)求。

此方法的缺省實(shí)現(xiàn)產(chǎn)生一個(gè)包含所有在 trace 請(qǐng)求中的 header 的信息的應(yīng)答(response)。在開發(fā) servlet 時(shí),多數(shù)情況下需要重載此方法。

此方法的格式:

protected void doTrace (HttpServletResquest request, HttpServletResponse response)
throws ServletException,IOException;

在開發(fā)以 HTTP 為基礎(chǔ)的 servlet 中,Servlet 開發(fā)者關(guān)心方法 doGet 和方法 doPost 即可。

三、Servlet 的生命周期

如果讀者寫過 Java 的小應(yīng)用程序(Applet),那 Servlet 對(duì)你來說就不會(huì)太難,也許更為簡(jiǎn)單。因?yàn)?Servlet 不用考慮圖形界面的應(yīng)用。與小應(yīng)用程序一樣,Servlet 也有一個(gè)生命周期。Servlet 的生命周期是當(dāng)服務(wù)器裝載運(yùn)行 servlets:接收來自客戶端的多個(gè)請(qǐng)求并且返回?cái)?shù)據(jù)給客戶端,然后再刪除移開 servlets。下面詳細(xì)描述如下:

1.初始化時(shí)期 當(dāng)一個(gè)服務(wù)器裝載 servlet 時(shí),它運(yùn)行 servlet 的 init() 方法。

public void init(ServletConfig config) throws ServletException
{
super.init(); //一些初始化的操作,如數(shù)據(jù)庫的連接
}

需要記住的是一定要在 init()結(jié)束時(shí)調(diào)用 super.init()。init()方法不能反復(fù)調(diào)用,一旦調(diào)用就是重裝載 servlet。直到服務(wù)器調(diào)用 destroy 方法卸載 servlet 后才能再調(diào)用。

2.Servlet 的執(zhí)行時(shí)期 在服務(wù)器裝載初始化 servlet 后,servlet 就能夠處理客戶端的請(qǐng)求,我們可以用 service 方法來實(shí)現(xiàn)。每個(gè)客戶端請(qǐng)求有它自己 service 方法:這些方法接收客戶端請(qǐng)求,并且發(fā)回相應(yīng)的響應(yīng)。Servlets 能同時(shí)運(yùn)行多個(gè) service。這是很重要的,這樣,service 方法可以按一個(gè)thread-safe 樣式編寫。如:service 方法更新 servlet 對(duì)象中的一個(gè)字段 field,這個(gè)字段是可以同時(shí)存取的。假如某個(gè)服務(wù)器不能同時(shí)并發(fā)運(yùn)行 service 方法,也可以用SingleThreadModel 接口。這個(gè)接口保證不會(huì)有兩個(gè)以上的線程(threads)并發(fā)運(yùn)行。在 Servlet 執(zhí)行期間其最多的應(yīng)用是處理客戶端的請(qǐng)求并產(chǎn)生一個(gè)網(wǎng)頁。其代碼如下:

PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>"# Servlet </title></head>");
out.println("<body>");
out.println("Hello World");
out.println("</body></html>");
out.close();

3.Servlet 結(jié)束時(shí)期 Servlets 一直運(yùn)行到他們被服務(wù)器卸載。在結(jié)束的時(shí)候需要收回在 init()方法中使用的資源,在 Servlet 中是通過 destory()方法來實(shí)現(xiàn)的。

public void destroy()
{
//回收在init()中啟用的資源,如關(guān)閉數(shù)據(jù)庫的連接等。
}

JSP 與 servlet 之間是怎樣的關(guān)系?

JSP 主要關(guān)注于 HTML(或者 XML)與 Java 代碼的結(jié)合,以及加入其中的 JSP 標(biāo)記。如果一個(gè)支持 JSP 的服務(wù)器遇到一個(gè) JSP 頁面,它首先查看該頁面是否被編譯成為一個(gè) servlet。由此可見,JSP 被編譯成 servlet,即被轉(zhuǎn)變?yōu)榧?Java,然后被裝載入服務(wù)器執(zhí)行。當(dāng)然,這一過程,根據(jù)不同的 JSP 引擎而略有不同。

JSP 和 servlet 在應(yīng)用上有什么區(qū)別

簡(jiǎn)單的說,SUN 首先發(fā)展出 SERVLET,其功能比較強(qiáng)勁,體系設(shè)計(jì)也很先進(jìn),只是,它輸出 HTML 語句還是采用了老的 CGI 方式,是一句一句輸出,所以,編寫和修改 HTML 非常不方便。

后來 SUN 推出了類似于 ASP 的嵌套型的 JSP,把 JSP TAG 嵌套到 HTML 語句中,這樣,就大大簡(jiǎn)化和方便了網(wǎng)頁的設(shè)計(jì)和修改。新型的網(wǎng)絡(luò)語言如 ASP,PHP 都是嵌套型的。

從網(wǎng)絡(luò)三層結(jié)構(gòu)的角度看,一個(gè)網(wǎng)絡(luò)項(xiàng)目最少分三層:data layer,business layer,,presentation layer。當(dāng)然也可以更復(fù)雜。

SERVLET 用來寫 business layer 是很強(qiáng)大的,但是對(duì)于寫 presentation layer 就很不方便。JSP 則主要是為了方便寫 presentation layer 而設(shè)計(jì)的。當(dāng)然也可以寫 business layer。寫慣了 ASP,PHP,CGI 的朋友,經(jīng)常會(huì)不自覺的把 presentation layer 和 business layer 混在一起。比如把數(shù)據(jù)庫處理信息放到 JSP 中,其實(shí),它應(yīng)該放在 business layer 中。

根據(jù) SUN 自己的推薦,JSP 中應(yīng)該僅僅存放與 presentation layer 有關(guān)的部分,也就是說,只放輸出 HTML 網(wǎng)頁的部份。而所有的數(shù)據(jù)計(jì)算、數(shù)據(jù)分析、數(shù)據(jù)庫聯(lián)結(jié)處理,統(tǒng)統(tǒng)是屬于 business layer,應(yīng)該放在 JAVA BEANS 中。通過 JSP 調(diào)用 JAVA BEANS,實(shí)現(xiàn)兩層的整合。

實(shí)際上,微軟前不久推出的 DNA 技術(shù),簡(jiǎn)單說,就是 ASP+COM/DCOM 技術(shù)。與 JSP+BEANS 完全類似,所有的 presentation layer 由 ASP 完成,所有的 business layer 由 COM/DCOM 完成。通過調(diào)用,實(shí)現(xiàn)整合。

為什么要采用這些組件技術(shù)呢?因?yàn)閱渭兊?ASP/JSP 語言是非常低效率執(zhí)行的,如果出現(xiàn)大量用戶點(diǎn)擊,純 SCRIPT 語言很快就到達(dá)了他的功能上限,而組件技術(shù)就能大幅度提高功能上限,加快執(zhí)行速度。

另外一方面,純 SCRIPT 語言將 presentation layer 和 business layer 混在一起,造成修改不方便,并且代碼不能重復(fù)利用。如果想修改一個(gè)地方,經(jīng)常會(huì)牽涉到十幾頁 CODE,采用組件技術(shù)就只改組件就可以了。

綜上所述,SERVLET 是一個(gè)不完善的產(chǎn)品,寫 business layer 很好,寫 presentation layer 就很遜色許多了,并且兩層混雜。所以,推出 JSP+BAEN,用 JSP 寫 presentation layer,用 BAEN 寫 business layer。SUN 自己的意思也是將來用 JSP 替代 SERVLET。

所以,學(xué)了 JSP,不會(huì)用 JAVA BEAN 并進(jìn)行整合,等于沒學(xué)。

如何調(diào)用 servlet?

要調(diào)用 Servlet 或 Web 應(yīng)用程序,請(qǐng)使用下列任一種方法:由 URL 調(diào)用、在

標(biāo)記中調(diào)用、在標(biāo)記中調(diào)用、在 ASP 文件中調(diào)用。

1.由 URL 調(diào)用 Servlet

這里有兩種用 Servlet 的 URL 從瀏覽器中調(diào)用該 Servlet 的方法:

(1)指定 Servlet 名稱:當(dāng)用 WebSphere 應(yīng)用服務(wù)器管理器來將一個(gè) Servlet 實(shí)例添加(注冊(cè))到服務(wù)器配置中時(shí),必須指定"Servlet 名稱"參數(shù)的值。例如,可以指定將 hi 作為 HelloWorldServlet 的 Servlet 名稱。要調(diào)用該 Servlet,需打開 http: //your.server.name/servlet/hi。也可以指定 Servlet 和類使用同一名稱(HelloWorldServlet)。在這種情況下,將由 http://your.server.name/servlet/ HelloWorldServlet 來調(diào)用 Servlet 的實(shí)例。

(2)指定 Servlet 別名:用 WebSphere 應(yīng)用服務(wù)器 管理器來配置 Servlet 別名,該別名是用于調(diào)用 Servlet 的快捷 URL??旖?URL 中不包括 Servlet 名稱。

2.在標(biāo)記中指定 Servlet

可以在標(biāo)記中調(diào)用 Servlet。HTM 格式使用戶能在 Web 頁面(即從瀏覽器)上輸入數(shù)據(jù),并向 Servlet 提交數(shù)據(jù)。例如:

<FORM METHOD="GET" ACTION="/servlet/myservlet">
<OL>
<INPUT TYPE="radio" NAME="broadcast" VALUE="am">AM<BR>
<INPUT TYPE="radio" NAME="broadcast" VALUE="fm">FM<BR>
</OL>
(用于放置文本輸入?yún)^(qū)域的標(biāo)記、按鈕和其它的提示符。)
</FORM>

ACTION 特性表明了用于調(diào)用 Servlet 的 URL。關(guān)于 METHOD 的特性,如果用戶輸入的信息是通過 GET 方法向 Servlet 提交的,則 Servlet 必須優(yōu)先使用 doGet()方法。反之,如果用戶輸入的信息是通過 POST 方法向 Servlet 提交的,則 Servlet 必須優(yōu)先使用 doPost()方法。使用 GET 方法時(shí),用戶提供的信息是查詢字符串表示的 URL 編碼。無需對(duì) URL 進(jìn)行編碼,因?yàn)檫@是由表單完成的。然后 URL 編碼的查詢字符串被附加到 Servlet URL 中,則整個(gè) URL 提交完成。URL 編碼的查詢字符串將根據(jù)用戶同可視部件之間的交互操作,將用戶所選的值同可視部件的名稱進(jìn)行配對(duì)。例如,考慮前面的 HTML 代碼段將用于顯示按鈕(標(biāo)記為 AM 和 FM),如果用戶選擇 FM 按鈕,則查詢字符串將包含 name=value 的配對(duì)操作為 broadcast= fm。因?yàn)樵谶@種情況下,Servlet 將響應(yīng) HTTP 請(qǐng)求,因此 Servlet 應(yīng)基于 HttpServlet 類。Servlet 應(yīng)根據(jù)提交給它的查詢字符串中的用戶信息使用的 GET 或 POST 方法,而相應(yīng)地使用 doGet() 或 doPost() 方法。

3.在標(biāo)記中指定 Servlet

當(dāng)使用標(biāo)記來調(diào)用 Servlet 時(shí),如同使用標(biāo)記一樣,無需創(chuàng)建一個(gè)完整的 HTML 頁面。作為替代,Servlet 的輸出僅是 HTML 頁面的一部分,且被動(dòng)態(tài)嵌入到原始 HTML 頁面中的其它靜態(tài)文本中。所有這些都發(fā)生在服務(wù)器上,且發(fā)送給用戶的僅是結(jié)果 HTML 頁面。建議在 Java 服務(wù)器頁面(JSP)文件中使用 標(biāo)記。

原始 HTML 頁面中包含標(biāo)記。Servlet 將在這兩個(gè)標(biāo)記中被調(diào)用,且 Servlet 的響應(yīng)將覆蓋這兩個(gè)標(biāo)記間的所有東西和標(biāo)記本身。如果用戶的瀏覽器可以看到HTML源文件,則用戶將看不到標(biāo)記。要在 Domino Go Webserver 上使用該方法,請(qǐng)啟用服務(wù)器上的服務(wù)器端包括功能。部分啟用過程將會(huì)涉及到添加特殊文件類型 SHTML。當(dāng) Web 服務(wù)器接收到一個(gè)擴(kuò)展名為 SHTML 的 Web 頁面請(qǐng)求時(shí),它將搜索 標(biāo)記。對(duì)于所有支持的 Web 服務(wù)器,WebSphere 應(yīng)用服務(wù)器將處理 SERVLET 標(biāo)記間的所有信息。下列 HTML 代碼段顯示了如何使用該技術(shù)。

<SERVLET NAME="myservlet" CODE="myservlet.class" CODEBASE="url" initparm1= "value">
<PARAM NAME="parm1" VALUE="value">
</SERVLET>

使用 NAME 和 CODE 屬性帶來了使用上的靈活性。可以只使用其中一個(gè)屬性,也可以同時(shí)使用兩個(gè)屬性。NAME 屬性指定了 Servlet 的名稱(使用 WebSphere 應(yīng)用服務(wù)器管理器配置的),或不帶.class 擴(kuò)展名的 Servlet 類名。CODE 屬性指定了 Servlet 類名。使用 WebSphere 應(yīng)用服務(wù)器時(shí),建議指定 NAME 和 CODE,或當(dāng) NAME 指定了 Servlet 名稱時(shí),僅指定 NAME。如果僅指定了 CODE,則會(huì)創(chuàng)建一個(gè) NAME=CODE 的 Servlet 實(shí)例。裝入的 Servlet 將假設(shè) Servlet 名稱與 NAME 屬性中指定的名稱匹配。然后,其它 SHTML 文件可以成功地使用 NAME 屬性來指定 Servlet 的名稱,并調(diào)用已裝入的 Servlet。NAME 的值可以直接在要調(diào)用 Servlet 的 URL 中使用。如果 NAME 和 CODE 都存在,且 NAME 指定了一個(gè)現(xiàn)有 Servlet,則通常使用 NAME 中指定的 Servlet。由于 Servlet 創(chuàng)建了部分 HTML 文件,所以當(dāng)創(chuàng)建 Servlet 時(shí),將可能會(huì)使用 HttpServlet 的一個(gè)子類,并優(yōu)先使用 doGet()方法(因?yàn)?GET 方法是提供信息給 Servlet 的缺省方法)。另一個(gè)選項(xiàng)是優(yōu)先使用 service()方法。另外,CODEBASE 是可選的,它指定了裝入 Servlet 的遠(yuǎn)程系統(tǒng)的 URL。請(qǐng)使用 WebSphere 應(yīng)用服務(wù)器管理器來從 JAR 文件配置遠(yuǎn)程 Servlet 裝入系統(tǒng)。

在上述的標(biāo)記示例中,initparm1 是初始化參數(shù)名,value 是該參數(shù)的值??梢灾付ǘ鄠€(gè)"名稱-值"對(duì)的集合。利用 ServletConfig 對(duì)象(被傳遞到 Servlet 的 init()方法中)的 getInitParameterNames()和 getInitParameter()方法來查找參數(shù)名和參數(shù)值的字符串?dāng)?shù)組。在示例中,parm1 是參數(shù)名,并在初始化 Servlet 后被才被設(shè)置某個(gè)值。因?yàn)橹荒芡ㄟ^使用"請(qǐng)求"對(duì)象的方法來使用以標(biāo)記設(shè)置的參數(shù),所以服務(wù)器必須調(diào)用 Servlet service()方法,以從用戶處傳遞請(qǐng)求。要獲得有關(guān)用戶的請(qǐng)求信息,請(qǐng)使用 getParameterNames()、getParameter() 和 getParameterValues()方法。

初始化參數(shù)是持續(xù)的。假設(shè)一臺(tái)客戶機(jī)通過調(diào)用一個(gè)包含某些初始化參數(shù)的 SHTML 文件來調(diào)用 Servlet。并假設(shè)第二臺(tái)客戶機(jī)通過調(diào)用第二個(gè) SHTML 文件來調(diào)用同一個(gè) Servlet,且該 SHTML 中未指定任何初始化參數(shù)。那么第一次調(diào)用 Servlet 時(shí)所設(shè)置的初始化參數(shù)將一直可用,并且通過所有其它 SHTML 文件而調(diào)用的所有后繼 Servlet 都不會(huì)更改該參數(shù)。直到 Servlet 調(diào)用了 destroy()方法后,才能重新設(shè)置初始化參數(shù)。例如,如果另一個(gè) SHTML 文件指定了另一個(gè)不同的初始化參數(shù)值,雖然已此時(shí)已裝入了 Servlet,但該值仍將被忽略。

4.在 ASP 文件中調(diào)用 Servlet 如果在 Microsoft Internet Information Server(IIS)上有遺留的 ASP 文件,并且無法將 ASP 文件移植成 JSP 文件時(shí),可用 ASP 文件來調(diào)用 Servlet。在 WebSphere 應(yīng)用服務(wù)器中的 ASP 支持包括一個(gè)用于嵌入 Servlet 的 ActiveX 控制,下面介紹 ActiveX 控制 AspToServlet 的方法和屬性。

該方法說明如下:

(1)String ExecServletToString(String servletName);執(zhí)行 ServletName,并將其輸出返回到一個(gè)字符串中。

(2)ExecServlet(String servletName);執(zhí)行 ServletName,并將其輸出直接發(fā)送至 HTML 頁面。

(3)String VarValue(String varName);獲得一預(yù)置變量值(其它格式)。

(4)VarValue(String varName, String newVal);設(shè)置變量值。變量占據(jù)的總大小應(yīng)小于0.5個(gè)千字節(jié)(Kbyte)。且僅對(duì)配置文件使用這些變量。

其屬性如下:

= Boolean WriteHeaders;若該屬性為真,則 Servlet 提供的標(biāo)題被寫入用戶處。缺省值為假。 = Boolean OnTest;若該屬性為真,服務(wù)器會(huì)將消息記錄到生成的 HTML 頁面中。缺省值為假。 下列 ASP 腳本示例是以 Microsoft Visual Basic Scripting(VBScript)書寫的。

<%
' Small sample asp file to show the capabilities of the servlets and the ASP GateWay ...
%>
<H1> Starting the ASP->Java Servlet demo</H1>
<%
' Create a Servlet gateway object and initialize it ...
Set Javaasp = Server.CreateObject("AspToServlet.AspToServlet")
' Setting these properties is only for the sake of demo.
' These are the default values ...
Javaasp.OnTest = False
Javaasp.WriteHeaders = False
' Add several variables ...
Javaasp.VarValue("gal") = "lag"
Javaasp.VarValue("pico")= "ocip"
Javaasp.VarValue("tal") = "lat"
Javaasp.VarValue("paz") = "zap"
Javaasp.VarValue("variable name with spaces") = "variable value with spaces"
%>
<BR>
Lets check the variables
<%
Response.Write("variable gal = ")
Response.Write(Javaasp.VarValue("gal"))
%>
<BR>
<%
Response.Write("variable pico = " & Javaasp.VarValue("pico"))
%>

<BR>
<HR>
<%
galout = Javaasp.ExecServletToString("SnoopServlet")
If Javaasp.WriteHeaders = True Then
%>
Headers were written <%
Else
%>
Headers were not written <%
End If
Response.Write(galout)
%>
<H1> The End ...</H1>

如何設(shè)置 servlet 類的路徑?

因?yàn)楦鱾€(gè)服務(wù)器對(duì)訪問 servlet 的策略不盡相同,所以在設(shè)置 servlet 類路徑時(shí)應(yīng)該視情況而定。 對(duì)于開發(fā)中的 servlet,只需確認(rèn)包含 Javax.servlet 的 JAR 文檔在您的類路徑中,并運(yùn)用如Javac 的普通開發(fā)工具。 對(duì)于 JSDK:JSDK_HOME/servlet.jar JSDK_HOME/server.jar 對(duì)于 Tomcat:TOMCAT_HOME/lib/servlet.jar 對(duì)于運(yùn)行中的 servlet,必須為 servlet 引擎設(shè)置類路徑,這根據(jù)不同的引擎,有不同的配置,如哪些庫和目錄應(yīng)包括,哪些不應(yīng)包括。注:對(duì)于 servlet 的動(dòng)態(tài)加載引擎如 JRun, Apache Jserv, Tomcat,包含 servlet 類文件的目錄不應(yīng)在類路徑中,而應(yīng)在 config 文件中配置。否則,servlet 可以運(yùn)行,但不能被動(dòng)態(tài)再加載。

Servlet 2.2 規(guī)范認(rèn)為以下應(yīng)被容器自動(dòng)包括,因此您不必把他們手工添加到類路徑。

· 所有的類應(yīng)放在 webapp/WEB-INF/classes 目錄下

· 所有 JAR 文件放在 webapp/WEB-INF/lib 目錄下

· 對(duì) webapps 的應(yīng)用體現(xiàn)在文檔系統(tǒng)中,對(duì)已打包進(jìn) JAR 文檔的 webapps 的應(yīng)用應(yīng)放入容器的 webapps 目錄。(例如,TOMCAT_HOME/webapps/myapp.jar)

另外,由 Gene McKenna(mckenna@meangene.com)撰寫的"The Complete CLASSPATH Guide for Servlets"詳細(xì)敘述了如何為 JavaWebServer 和 Jrun 設(shè)置類路徑。

如何實(shí)現(xiàn) servlet 與 applet 的通信?

這個(gè)例子將向讀者展示服務(wù)器端程序(Servlet)和小應(yīng)用程序(Applet)之間是如何完成通信活動(dòng)的。它由三個(gè)文件組成,一個(gè)是 sendApplet.Java 文件,用于實(shí)現(xiàn) Applet,一個(gè)是 receiveservlet.Java,用于實(shí)現(xiàn) servlet,還有一個(gè)是 add -servlet.html,用于調(diào)用 Applet。

在 sendApplet.Java 文件中,最重要的要屬 init()函數(shù)和 Send()函數(shù),其中 init()函數(shù)用來生成整個(gè) Applet 的用戶操作界面,包括消息文本框、發(fā)送按鈕等等。而消息的發(fā)送過程則由 Send()函數(shù)來完成。請(qǐng)仔細(xì)閱讀下面的代碼:

private void Send()
{
message = sendText.getText();
//清除用戶的輸入信息
sendText.setText("");
showStatus("Message send!");
//把輸入的字符串轉(zhuǎn)化為 x-www-form-urlencoded 格式
String queryString = "/servlet/ReceiveServlet?message=" + URLEncoder.encode ( message ) ;
p("Attempting to send:"+message);

//建立與Servlet的聯(lián)接,并取得Servelt的輸出信息
try {
connect = (new URL(chatURL,queryString)).openConnection();
showStatus("open connection!");
//下次連接不用Cache
connect.setDefaultUseCaches(false);
//這次連接也不用Cache
connect.setUseCaches(false);
//打開淂流用于讀數(shù)據(jù)
connect.setDoInput(true);
//不能用于寫數(shù)據(jù)
connect.setDoOutput(false);
//服務(wù)器與客戶的真正連接
connect.connect();
p("Made connection to "+connect);
showStatus("Open Stream!");
DataInputStream in = new DataInputStream(connect.getInputStream());
showStatus("reading!");
message = in.readLine();
while (message! = null)
{
//在消息文本框顯示Servlet生成的信息
messageText.setText(message);
message = in.readLine();
}
}catch(MalformedURLException e2)
{
System.err.println("MalformedURLException!");
e2.printStackTrace(System.err);
showStatus("MalformedURLException!");
}catch(IOException e1)
{
System.err.println("IOException!");
e2.printStackTrace(System.err);
showStatus("IOException");
}
}

整個(gè) Applet 的詳細(xì)代碼請(qǐng)見 sendApplet.Java。

當(dāng) Applet 與 Servlet 建立連接后,工作就可以交給 Servlet 了,由它來解析客戶端的請(qǐng)求,獲得參數(shù) message 的值,然后將適當(dāng)信息返回給客戶端,并由 Applet 進(jìn)行顯示。完成該功能的是 receiveservlet.Java 中的 service()函數(shù):

public void service (HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
{
res.setContentType("text/plain");
ServletOutputStream out = res.getOutputStream();
out.print("receive user message:");
out.print(req.getParameter("message"));
}

該 Servlet 的詳細(xì)源代碼請(qǐng)見 receiveservlet.Java。 最后一個(gè)文件是 add-servlet.html,它用來調(diào)用 Applet:

<html>
<head>
<title>sendApplet</title>
</head>
<body>
<hr>
<applet code=sendApplet width=400 height=300 ></applet>
<hr>
</body>
</html>

如何應(yīng)用應(yīng)用 Servlet 進(jìn)行圖象處理?

我們?cè)谔幚頂?shù)據(jù)時(shí),有時(shí)希望能用圖象直觀的表述,在這里有一個(gè)巧方法,能方便快捷的實(shí)現(xiàn)一些簡(jiǎn)單的圖形(不能稱之圖象),比如條形圖,我們不必去用 Java 來生成并顯示圖象,(Java 生成圖象很慢),我們可以這樣來作,先用作圖工具作一個(gè)很小的你需要的圖片,再根據(jù)你所處理的數(shù)據(jù)量來實(shí)時(shí)的加長(zhǎng)它,就可以得到所要表述的圖例。比如我們?cè)跀?shù)據(jù)庫中得到了一組數(shù)據(jù),我們從中找出最大的那一個(gè),按比列設(shè)定其標(biāo)簽的長(zhǎng)度,其它的數(shù)據(jù)圖形則可與它相比,得到的長(zhǎng)度,這樣,一個(gè)簡(jiǎn)簡(jiǎn)單單的條形圖就出來。但有時(shí)一些簡(jiǎn)單的圖形已經(jīng)不能解決我們實(shí)際遇到的情況,比如曲線圖就不能用這種方法,這時(shí)我們需要生成 Java 圖象,也許大家都用過 applet 這樣的程序吧,若訪問量不大,而實(shí)時(shí)性又很特殊時(shí)(比如股票系統(tǒng)),必須這樣用它。但事實(shí)上,我們 web 程序大多有前后臺(tái)之分,前臺(tái)瀏覽,后臺(tái)維護(hù)。這樣我們可以在后臺(tái)用 servlet 實(shí)時(shí)動(dòng)態(tài)定時(shí)地生成圖象文件,而前臺(tái)只是查看靜態(tài)圖片,這比你用 applet 來動(dòng)態(tài)產(chǎn)生圖象的速度快了不知多少倍,因?yàn)?applet 來動(dòng)態(tài)產(chǎn)生圖象,有兩個(gè)地方很費(fèi)時(shí),一是數(shù)據(jù)庫查詢時(shí)間,二是 applet 本身生成圖象就很慢。下面以一個(gè)簡(jiǎn)單的例子來說明一下怎樣生成并寫入圖象文件,本例注重的是怎樣寫入圖象文件,相信寫過 applet 的讀者會(huì)生成更加漂亮的圖象。

package test;
import Javax.servlet.*;
import Javax.servlet.http.*;
import Java.io.*;
import Java.util.*;
import Java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.*;
import Java.awt.image.*;
import Java.awt.*;

public class Servlet2 extends HttpServlet
{
public void init(ServletConfig config) throws ServletException {
super.init(config);
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String sFileName = "e:/temp/name.jpg";
try{
FileOutputStream fos = new
FileOutputStream(sFileName);
BufferedImage myImage = new BufferedImage(225, 225,BufferedImage. TYPE_INT_RGB);
Graphics g = myImage.getGraphics();
g.setColor(Color.white);
g.fillRect(0,0,225,225);
g.setColor(Color.black);
g.drawString("Finance Balance Summary", 40, 15);
g.drawString("Primary", 90, 30);
g.setColor(Color.darkGray);
g.fillRect(15,193,7,7);
g.setColor(Color.black);
g.drawString("% Operating", 25, 200);
g.setColor(Color.yellow);
g.fillRect(130,193,7,7);
g.setColor(Color.black);
g.drawString("% Term", 140, 200);
g.setColor(Color.lightGray);
g.fillRect(15,213,7,7);
g.setColor(Color.black);
g.drawString("% Mortgage", 25, 220);
g.setColor(Color.green);
g.fillRect(130,213,7,7);
g.setColor(Color.black);
g.drawString("% Lease", 140, 220);
JPEGImageEncoder jpg = JPEGCodec.createJPEGEncoder(fos);
jpg.encode(myImage);
}catch (Exception e)
{
String exceptionThrown = e.toString();
String sourceOfException = " Method";
System.out.println("Origional Exception Thrown: " +exceptionThrown + '/r' + '/n');
System.out.println("Origional SourceOfException: " + sourceOfException +'/r' + '/n');
} // CatchStatementEnd
}
}

如何通過 Servlet 調(diào)用 JavaBean 輸出結(jié)果集

以此我們通過一個(gè)例子進(jìn)行說明,該例演示了如何通過 Servlet 調(diào)用 JavaBean 輸出結(jié)果集,并打印的方法,共由兩個(gè)文件組成,一個(gè)是 JavaBean,用于實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的訪問,并獲得結(jié)果集;另一個(gè)是 Servlet,主要負(fù)責(zé) JavaBean 的調(diào)用,并將結(jié)果集發(fā)送到客戶端。

在 JavaBean 中,我們將訪問 DB2 樣例數(shù)據(jù)庫(sample)中的 STAFF 表,至于如何實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的訪問,讀者可以參考《JSP 與 JDBC》一章。此外,讀者可以通過修改部分參數(shù),來實(shí)現(xiàn)對(duì)其他數(shù)據(jù)庫、表的訪問,達(dá)到舉一反三的效果。

該 JavaBean 的核心是 execute()函數(shù):

public void execute()
{
try {
//裝載JDBC驅(qū)動(dòng)程序
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
//建立對(duì)數(shù)據(jù)庫的連接
conn = DriverManager.getConnection("jdbc:db2:sample", "db2admin", "db2admin");
stmt = conn.createStatement();
String sql = "SELECT * FROM STAFF WHERE DEPT=20";
//執(zhí)行查詢語句,返回結(jié)果集
ResultSet rs = stmt.executeQuery(sql);
setResult(rs);
} catch (SQLException e) {
} catch (IllegalAccessException e2) {
} catch (ClassNotFoundException e3) {
} catch (InstantiationException e4) {}
}

JavaBean 的具體源代碼請(qǐng)見 Tbean.Java。

知道數(shù)據(jù)是如何獲取之后,下面我們來看一下Servlet 是如何來調(diào)用上述 JavaBean 的。

同樣看 service()方法即可(詳細(xì)源代碼請(qǐng)見 Tservlet.Java):

public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
try {
//實(shí)例化JavaBean
Demo.TBean Javabean = new Demo.TBean();
Javabean.execute();
ResultSet rs1 = Javabean.getResult();
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<table border=1>"=;
out.println("<H1>Hello World</H1>"=;
out.println("<td>ID</td><td>NAME</td><td>DEPT</td><td>JOB</td><td>YEARS</td><td>SALARY</td><td>COMM</td>"=;
while (rs1.next())
{
out.println("<tr>"=;
for (int i = 1; i <= 7; i++=
out.println("<td>" + rs1.getString(i) + "</td>"=;
out.println("</tr>"=;
}
out.println("</table>"=;
Javabean.Sqlclose();
} catch (SQLException e) {}
}

//運(yùn)行:在 VisualAge for Java 的 IBM Websphere Test Environment 的環(huán)境下: //http://localhost:8080/servlet/Demo.TServlet

如何用 Servlet 來中斷涉及的多線程

現(xiàn)在我們已經(jīng)知道,當(dāng)服務(wù)器要卸載一個(gè) Servlet 時(shí),它會(huì)在所有的 service 都已經(jīng)完成后再調(diào)用 destroy()方法。如果程序的操作運(yùn)行需要很長(zhǎng)時(shí)間,destroy()被調(diào)用時(shí)就可能還有其他線程在運(yùn)行。Servlet 程序員必須保證所有的線程都已經(jīng)完成。

長(zhǎng)時(shí)間運(yùn)行響應(yīng)客戶端請(qǐng)求的那些 Servlet 應(yīng)當(dāng)保留當(dāng)前有多少方法在運(yùn)行的記錄。它的 long-running 方法應(yīng)當(dāng)周期性地輪流詢問以確保它們能夠繼續(xù)運(yùn)行下去。如果 Servlet 被 destroy()方法調(diào)用,那么這個(gè) long-running 方法必須停止工作或清除。

舉例,變量 serviceCounter 用來統(tǒng)計(jì)有多少 service 方法在運(yùn)行,變量 shuttingDown 顯示這個(gè) Servlet 是否被 destroy。每個(gè)變量有它自己的獲取方法:

public ShutdownExample extends HttpServlet
{
private int serviceCounter = 0;
private Boolean shuttingDown;
…
//serviceCounter
protected synchronized void enteringServiceMethod()
{
serviceCounter++;
}
protected synchronized void leavingServiceMethod()
{
serviceCounter--;
}
protected synchronized int numServices()
{
return serviceCounter;
}
//shuttingDown
protected setShuttingDown(Boolean flag)
{
shuttingDown = flag;
}
protected Boolean isShuttingDown()
{
return shuttingDown;
}

這個(gè) service 方法每次在它進(jìn)入時(shí)要增加,而在它返回退出時(shí)要減少:

protected void service(HttpServletRequest req , HttpServletResponse resp)
throws ServletException IOException
{
enteringServiceMethod();
try{
super.service(req , resp);
}
finally {leavingServiceMethod();}
}

destroy 方法應(yīng)當(dāng)檢查 serviceCounter,如果存在長(zhǎng)時(shí)間方式運(yùn)行的話,設(shè)置變量 shuttingDown。這個(gè)變量將會(huì)讓那個(gè)正在處理請(qǐng)求的線程知道該結(jié)束了。destroy 方法應(yīng)當(dāng)?shù)却@幾個(gè) service 方法完成,這樣就是一個(gè)清楚的關(guān)閉過程了。

public void destroy()
{
//檢查是否有線程在運(yùn)行,如果有,告訴它們停止
if (numServices() > 0)
{
setShuttingDown(true);
}
//等待它們停止
while(numService() > 0)
{
try{
thisThread.sleep(interval);
}catch(InterruptedException e) {}
}
}

long-running 方法如必要應(yīng)當(dāng)檢查這個(gè)變量,并且解釋它們的工作:

public void doPost(…)
{
…
for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++)
{
try{
partOfLongRunningOperation(i);
}catch (InterruptedException e) {}
}
}
上一篇:Netty下一篇:集合類