日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java Servlet 技术简介

發布時間:2025/3/21 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java Servlet 技术简介 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開始之前

關于本教程

在您最喜歡的 Web 瀏覽器中,您所閱讀的頁面是如何出現的呢?當登錄到您最喜歡的 Web 站點時,該 Web 站點如何知道登錄的用戶是您?而 Web 零售商又如何接受您的在線訂購呢?這些功能都是可能的,因為在這些場景的背后,運行于服務器上的代碼將在 Web 會話中與您進行交互,通過該過程訪問已存儲的信息,并經常在一個或多個 Web 頁面中展示動態信息。在 Java 語言世界中,這些功能的核心部分是由?servlet?提供的。本教程的目的就是向您介紹 servlet。文中將描述 servlet 是什么,它們是如何工作的,您可以如何使用它們來創建您能夠想像到的任意復雜度的 Web 應用程序,以及作為一名專業編程人員,您如何才能最有效地使用 servlet。

本教程的內容是為不熟悉,或者只是略微熟悉 servlet 的 Java 編程人員準備的。本教程假定您對于下載和安裝軟件以及 Java 語言(創建類、導入類等)有一般性的了解,但并不假定您已經了解 servlet。本教程包括一個說明 servlet 基本概念的簡單例子,以及一個涉及更多內容的例子,它說明如何在小型的合同管理程序中更復雜地使用 servlet。

本教程的適用對象

如果您已編寫 Web 應用程序多年,那么本教程可能不適合您。如果您不知道 servlet 是什么,或者只是略懂一二,那么請您繼續讀下去。雖然本教程所包含的只是 servlet 的部分內容,但它是一篇很好的入門介紹。

不過,您應該非常了解 Java 編程的基礎知識。但是,如果您還沒有完全達到這些要求,那么請從閱讀我撰寫的?Java 編程簡介?教程開始。

工具和代碼

為了運行本教程中的例子或示例代碼,至少需要在機器上安裝 JDK 1.4.2 或更高版本,以及 Eclipse IDE。我們將介紹安裝用于 Eclipse 的 Tomcat 插件的整個過程,這將允許您很容易地開發 servlet 應用程序。

本教程中的所有代碼示例都在 Windows XP 平臺上用 J2SE 1.4.2 進行了測試,但必須使用 J2SE 1.4.1 或者甚至是 5.0 版本,并且不對它們進行修改,它們才會工作。

要安裝 Tomcat,則需要進入 Jakarta 的 Web 站點(請參閱?參考資料),并下載二進制版本的 Tomcat 5.0.28(編寫本教程時,這是匹配 J2SE 1.4.2 的最新版本)。隨 Windows 安裝程序一起提供的包會使該平臺上的安裝輕而易舉地完成。按照 readme 文件中的說明進行安裝,您將順利完成這項操作。

為了安裝用于 Eclipse 的 Tomcat 插件,需要進入 Sysdeo 的 Web 站點(請參閱?參考資料?),并下載該插件的 zip 文件(編寫本教程時,該文件是 tomcatPluginV3.zip)。然后只要將之解壓至 plugins 目錄,并按照下載頁面底部的說明安裝該插件即可。為了確保這個插件正常工作,請閱讀極為簡單的 HelloWorld servlet 設置“教程”,Sysdeo 頁面底部有其鏈接(至于直接鏈接,請參閱?參考資料)。

一旦安裝了 Tomcat 及其插件,就可以準備開始本教程了。

servlet 簡介

servlet 的作用

當使用交互式 Web 站點時,您所看到的所有內容都是在瀏覽器中顯示的。在這些場景背后,有一個 Web 服務器接收會話?中來自于您的請求,可能要切換到其他代碼(可能位于其他服務器上)來處理該請求和訪問數據,并生成在瀏覽器中顯示的結果。

servlet 就是用于該過程的網守(gatekeeper)。它駐留在 Web 服務器上,處理新來的請求和輸出的響應。它與表示無關,實際上也不它應該與表示有關。您可以使用 servlet 編寫一個,將內容添加到 Web 頁面中,但那通常也不是一個好辦法,因為它有鼓勵表示與業務邏輯的混合的傾向。

servlet 的替代品

servlet 不是服務于 Web 頁面的惟一方式。滿足該目的的最早技術之一是公共網關接口(CGI),但那樣就要為每個請求派生不同的進程,因而會影響效率。還有專用服務器擴展,如 Netscape Server API(NSAPI),但那些都是完全專用的。在 Microsoft 的世界里,有活動服務器頁面(ASP)標準。servlet 為所有這些提供了一個替代品,并提供了一些好處:

  • 它們與 Java 語言一樣是與平臺無關的。
  • 它們允許您完全訪問整個 Java 語言 API,包括數據訪問庫(如 JDBC)。
  • 大多數情況下,它們內在地比 CGI 更高效,因為 servlet 為請求派生新的線程,而非不同的進程。
  • 對于 servelet 有一個廣泛的行業支持,包括用于最流行的 Web 和應用程序服務器的容器

servlet 是對專業編程人員工具箱的強大補充。

但什么是 servlet?

作為一名專業編程人員,您碰到的大多數 Java servlet 都是為響應 Web 應用程序上下文中的 HTTP 請求而設計的。因此,javax.servlet?和?javax.servlet.http?包中特定于 HTTP 的類是您應該關心的。

在創建一個 Java servlet 時,一般需要子類?HttpServlet。該類中的方法允許您訪問請求和響應包裝器(wrapper),您可以用這個包裝器來處理請求和創建響應。

當然,HTTP 協議不是特定于 Java 的。它只是一個規范,定義服務請求和響應的大致式樣。Java servlet 類將那些低層的結構包裝在 Java 類中,這些類所包含的便利方法使其在 Java 語言環境中更易于處理。正如您正使用的特定 servlet 容器的配置文件中所定義的,當用戶通過 URL 發出一個請求時,這些 Java servlet 類就將之轉換成一個?HttpServletRequest,并發送給 URL 所指向的目標。當服務器端完成其工作時,Java 運行時環境(Java Runtime Environment)就將結果包裝在一個?HttpServletResponse?中,然后將原 HTTP 響應送回給發出該請求的客戶機。在與 Web 應用程序進行交互時,通常會發出多個請求并獲得多個響應。所有這些都是在一個會話語境中,Java 語言將之包裝在一個?HttpSession?對象中。在處理響應時,您可以訪問該對象,并在創建響應時向其添加事件。它提供了一些跨請求的語境。

容器(如 Tomcat)將為 servlet 管理運行時環境。您可以配置該容器,定制 J2EE 服務器的工作方式,而且您必須?配置它,以便將 servlet 暴露給外部世界。正如我們將看到的,通過該容器中的各種配置文件,您在 URL(由用戶在瀏覽器中輸入)與服務器端組件之間搭建了一座橋梁,這些組件將處理您需要該 URL 轉換的請求。在運行應用程序時,該容器將加載并初始化 servlet,管理其生命周期

當我們說 servlet 具有生命周期時,只是指在調用 servlet 時,事情是以一種可預見的方式發生的。換言之,在任何 servlet 上創建的方法總是按相同的次序被調用的。下面是一個典型場景:

  • 用戶在瀏覽器中輸入一個 URL。Web 服務器配置文件確定該 URL 是否指向一個由運行于服務器上的 servlet 容器所管理的 servlet。
  • 如果還沒有創建該 servlet 的一個實例(一個應用程序只有一個 servlet 實例),那么該容器就加載該類,并將之實例化。
  • 該容器調用 servlet 上的?init()。
  • 該容器調用 servlet 上的?service(),并在包裝的?HttpServletRequest?和?HttpServletResponse?中進行傳遞。
  • 該 servlet 通常訪問請求中的元素,代表其他服務器端類來執行所請求的服務并訪問諸如數據庫之類的資源,然后使用該信息填充響應。
  • 如果有必要,在 servlet 的有用生命結束時,該容器會調用 servlet 上的?destroy()?來清除它。

如何“運行”servlet

“運行”servlet 就像運行 Java 程序一樣。一旦配置了容器,使容器了解 servlet,并知道某些 URL 會致使容器調用該 servlet,該容器就將按照預定的次序調用生命周期方法。因此,運行 servlet 主要是指正確配置它,然后將瀏覽器指向正確的 URL。當然,servlet 中的代碼正是發現有趣的業務邏輯的地方。您不必擔心低層事件的進展,除非發生某種錯誤。

不幸的是,經常會發生?一些令人沮喪的錯誤,尤其是在設置 servlet 時。致使 servlet 應用程序令人頭痛的最大原因就是配置文件。您無法有效地調試它們。您只能通過試錯法弄清楚這些錯誤,比如盡力破譯可能會或不會在瀏覽器中看到的錯誤消息。

一個簡單的 servlet

這個簡單的 servlet 要完成的任務

第一個 servlet 將完成極少量的工作,但是它將暴露編寫 servlet 的所有基本要求。它將在瀏覽器窗口中輸出一些簡單的無格式文本:

Hello, World!

在創建該 servlet 時,我們將可以證實 Tomcat 應起的作用,并證實我們可以按照計劃使用 Eclipse 創建 Web 項目。我們還將遍歷在 Tomcat servlet 容器中配置 Web 應用程序的整個過程,如果您碰巧在 XML 文件中犯了一個小錯誤,那么您可能會對這個過程感興趣。不要擔心:至少在本教程中,Tomcat 會一直發揮起作用。

在這第一個例子中,我們會將輸出直接從 servlet 寫入瀏覽器中。在本教程中,這將是我們最后一次使用該方法。

設置 Eclipse

我們需要執行少量工作,確保可以在 Eclipse 中創建并管理 Tomcat 項目。

如果已經安裝了該插件(僅僅通過將 Sysdeo zip 文件解壓至 eclipse/plugins 目錄),那么您應該可以在工具欄上獲得一些附加的菜單項和工具。如圖 1 中所示。

圖 1. Tomcat 插件功能

工具欄按鈕允許您啟動、停止和重啟 Tomcat,當需要運行 servlet 時,必須進行這些工作。

為了允許我們創建 Tomcat 項目,這些項目具有合適的布局,有助于 Tomcat 的部署,我們必須告訴 Eclipse 一些事情。如果單擊?Window>Preferences,那么您就將看到標準的 Eclipse 偏好設定對話框,其列表底部有一個名為?Tomcat?的新類別。單擊它將向您展示 Tomcat 偏好設定的主頁(參見圖 2)。

圖 2. Tomcat 的偏好設定

選擇?Version 5.x,并指定?Tomcat home?的位置。(我的系統上,該位置是 C:\Program Files\Apache Software Foundation\Tomcat 5.0,但您的可能會不同)。選擇?Context files?為內容聲明模式。然后,單擊?JVM Settings?對子類別進行偏好設定,并確保在該頁頂部的下拉菜單中選擇一個有效的 JRE。您可以使用默認的 JRE,也可以指向您的 JDK,并在?Java>Installed JREs?偏好設定頁面中告訴 Eclipse 這個 JDK。

完成這些操作后,請單擊?OK。現在,我們準備創建 Tomcat 項目。

創建 Tomcat 項目

Tomcat 插件使 Web 開發人員更易于使用 Tomcat。如果單擊?File>New>Project,并展開對話框(參見圖 3)中的?Java?向導類別,那么您會在該對話框中看到一類新的項目向導:Tomcat 項目。

圖 3. 新建 Tomcat 項目

單擊?Next,將該項目命名為“HelloWorld”,然后單擊?Finish。如果在 Eclipse 中切換至 Java 透視圖,那么就可以看到這個新項目。它所具有的結構將有助于部署 Tomcat(參見圖 4)。

圖 4. Tomcat 項目結構

正如我們稍后將看到的,work、WEB-INF 和 WEB-INF/src 目錄特別重要。

測試 Tomcat

單擊?Start Tomcat?工具欄按鈕。當 Tomcat 嘗試啟動時,Eclipse 將用信息語句更新控制臺。如果它啟動了,并且沒有顯示任何堆棧跟蹤,那么您已作好準備。如果看到堆棧跟蹤,則事情會麻煩一些。不幸的是,試錯法(通過您的好朋友 Google)是跟蹤所發生錯誤的惟一方法。好消息是:可以進行刷新,新的項目(如我們剛才創建的那個)將消除發生嚴重錯誤的可能性。

當 Tomcat 啟動時,您不會看到任何東西(除了控制臺內容)。您必須加以測試以確保它能工作。如果您需要一個快速指示,就設法打開瀏覽器并輸入下列 URL:

http://localhost:8080/

如果一切正常,您就將看到一個精致的 Tomcat 歡迎頁面,或一個列舉了 Tomcat“啟動內容”的目錄。不必關注第二個。當我們運行第一個 servlet 時,我們將證明 Tomcat 在工作。

聲明類

servlet 是一個類,因此,讓我們創建一個基本的。在 Eclipse 中,要在 HelloWorld 項目中創建一個名為?HelloWorldServlet?的類。該類如下所示:

public class HelloWorldServlet extends HttpServlet {public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {PrintWriter writer = response.getWriter();writer.println("Hello, World!");writer.close();} }

輸入這些代碼,然后按?Ctrl+Shift+O?組織一下導入語句。Eclipse 將允許您導入下列類:

  • java.io.IOException
  • java.io.PrintWriter
  • javax.servlet.ServletException
  • javax.servlet.HttpServlet
  • javax.servlet.HttpServletRequest
  • javax.servlet.HttpServletResponse

請注意,我們將?HttpServlet?作為子類,并重載了?service()?方法。service()?方法是 servlet 引擎將在 servlet 生命周期中調用的最基本的處理方法。它接收一個請求包裝器和一個響應包裝器,而我們可以在我們的方法中訪問它們。但在這里,我們不需要這樣做,因為我們只是進行一些基本操作來使 servlet 工作。我們可能重載了?doGet(),但是?service()?將提供我們所需要的東西。

在我們的?service()?方法中,我們在?response?包裝器上調用?getWriter(),以便能夠將一串文字輸出到輸出流中。然后,我們關閉輸出流。這在生成輸出的 servlet 中是很典型的:您執行需要執行的邏輯,然后寫入輸出流。

配置 Web 應用程序

Java 編程工作就完成了,但是現在,我們還必須對配置文件進行必要的工作。在我看來,這是 Web 開發的最大難點。幸好,該 Tomcat 插件分擔了部分重擔。

右擊 HelloWorld 項目,并選擇?Properties。選擇屬性的?Tomcat?類別。您將看到該項目的環境,如下所示:

/HelloWorld

現在,去查看您 Tomcat 原目錄中的文件系統。轉至 conf/Catalina/localhost 子目錄。在那里,您將看到一組 XML 文件。具體地說,您將看到一個 HelloWorld.xml 文件。打開它。該文件為 Tomcat 定義了一個 Web 應用程序語境。

<Context path="/HelloWorld" reloadable="true" docBase="path to your project\HelloWorld" workDir="path to your project\HelloWorld\work" />

當 Tomcat 啟動時,它讀取這些上下文文件,告訴 servlet 容器在哪里找您的類(包括 servlet)。如果回顧加載 Tomcat 時向控制臺發出的信息(INFO)語句,就會看到該列表中與 Web 應用程序上下文有關的信息。

在 Tomcat 中配置 Web 應用程序的最后一步是創建 web.xml 文件,需要將該文件放在項目的 WEB-INF 目錄中。(注意:不要?將其放在 WEB-INF/src 目錄中 —— 該目錄將包含其他東西。)對于這個簡單例子,該文件將如下所示:

<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'> <web-app><servlet><servlet-name>hello</servlet-name><servlet-class>HelloWorldServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping> </web-app>

該文件向 Tomcat 定義了 Web 應用程序。該文件中的?servlet-name?元素命名了所使用的 servlet。servlet-class?元素將該名稱映射到一個特定的類,該類定義了 servlet,即?HelloWorldServlet(本示例中)。servlet-mapping?元素告訴 Tomcat?/hello(本例中)形式的 URL 映射我們的 servlet,這是由映射的 servlet 類定義的。

一旦設置好該文件,就可以啟動 Tomcat 并能看到 servlet 加載。

運行 servlet

正如前面提到的,“運行 servlet”僅僅包括啟動 Tomcat,以及將 Web 瀏覽器指向將調用它的 URL。通過適當的工具欄按鈕啟動 Tomcat(如果它已經運行,則需要停止并重新啟動它)。一旦 Tomcat 結束啟動過程,它會打開瀏覽器并輸出下列 URL:

http://localhost:8080/HelloWorld/hello

您將在瀏覽器窗口中看到一條好消息。

動作 servlet

簡介

在 Web 開發初期,許多專業編程人員都不得不弄清當他們繼續時,如何較好地使用 servlet。最普遍的結果之一就是在服務器上暴露 servlet。每種類型的請求都有一個。

這很快就變得令人頭痛,因此,編程人員開始在其 servlet 中包含條件邏輯使之更具適應性,以便處理多種類型的請求。一段時間后,這也產生了一些糟糕的代碼。有一種更好的方式,稱作動作 servlet(action servlet),它實現了名為模型 2?的概念。據我了解,該思想是由 David M. Geary(關于他的更多信息,請參閱?參考資料)首次寫到的,但是它已經較好的用于流行的 servlet 庫中了,例如 Jakarta Struts 項目。

在動作 servlet 中,并沒有指示 servlet 行為的條件邏輯,而是具有動作(編程人員定義的類),servlet 授權這些類來處理不同類型的請求。大多數情況下,這個面向對象(OO)的方法要優于擁有多個 servlet,或在一個 servlet 中有多個?if?條件。

我們的示例動作 servlet 執行的操作

我們的示例動作 servlet 將是一個極簡單的、基于瀏覽器的應用程序的網守(gatekeeper),該應用程序將允許我們創建、存儲、查看以及刪除合同列表項。這些記錄項的格式都非常良好。最后,為了使用該應用程序,用戶將必須登錄它,但是,我們稍后將在?用戶和數據?中添加這項功能。

設置該項目

在 Eclipse 中創建一個新的 Tomcat 項目,就像您為 HelloWorld 所做的一樣。請注意,項目名稱就是 servlet 默認的上下文值,因此,當輸入訪問 servlet 的 URL 時,將使用它。如果配置 Tomcat 使用上下文文件,那么它將為該項目自動創建一個上下文值。

Eclipse 還應創建一個具有正確結構的項目,并帶有下列重要目錄:

  • WEB-INF
  • WEB-INF/src
  • work

第一個目錄(WEB-INF)存儲重要的配置文件,具體地說就是 web.xml 文件,我們稍后將討論它。它還在 classes 目錄中包含了編譯的代碼。第二個目錄(WEB-INF/src)存儲 Java 類的源代碼。第三個目錄(work)包含 JavaServer Pages(JSP)文件的編譯代碼,代碼發生更改之后,每當我們第一次點擊 JSP 頁面時,Tomcat 就會為我們自動創建這些編譯代碼(我們將在下一面板上談論更多 JSP 技術)。該項目的根目錄包含所有的 JSP 源文件,以及數據庫文件。

請注意,您可以在 Eclipse 的 Resource 視圖中看到該結構的所有東西,但是在 Java Browsing 視圖中只能看到 WEB-INF/src 和 work 目錄。

所有這些文件都包含在本教程所包括的 contacts.jar 文件中(有關鏈接,請參閱?參考資料)。為了導入它們,只要創建一個新的 Tomcat 項目,然后導入 contacts.jar(使用?Import>Zip file?選項)即可。這將會在正確的位置中產生除源代碼之外的所有文件。源代碼最終會在項目根目錄的 src 目錄中產生。將該文件夾的內容移至 WEB-INF/src 中,您就完成了所有的準備工作。

表示

這畢竟是一篇關于 servlet 的教程,幾乎與表示無關。然而,若不在屏幕某處看到一些結果,我們實際上就只告知了事情的部分內容。您當然可以編寫根本不涉及表示的 servlet,但是大多數 Web 應用程序在瀏覽器中顯示信息,這意味著您必須選擇使用一種表示機制。JavaServer Pages 技術就是一種典型的備選方案,并得到了廣泛采用。

通過 JSP 技術,您可以創建動態 Web 頁面。它們支持靜態 HTML(或其他標記,如 XML)和動態代碼元素,而正如名字所隱含的,動態代碼元素可以動態創建內容。在幕后,可以通過諸如 Tomcat 之類的容器將 JSP 頁面編譯成 servlet(即轉換成 Java 代碼)。然而,您幾乎永遠不必關心這一點。只需要知道發生了下列流程即可:

  • 用戶在瀏覽器中輸入 URL,J2EE servlet 容器將該瀏覽器指向一個 servlet。
  • servlet 完成其工作,并在會話中輸入信息,或者在 bean 中,再發送給 JSP 頁面。
  • JSP 代碼轉換 bean 和/或會話中的信息,并將響應發送給瀏覽器。

您可以很容易地創建簡單的 JSP 頁面,只需要在 Web 應用程序中進行微小的修改即可,并且無需下載額外的代碼庫,就可以在 Tomcat 中運行它們,因此,我們將在這里使用它們(關于 JSP 技術的更多詳細信息,請參閱?參考資料)。

我們的 Contacts 應用程序會有一個主要的 JSP 頁面,列舉現有的合同并添加新的合同。稍后,我們將添加用于登錄和退出頁面。

重要的是記得 JSP 技術只是一種表示選擇。還有其他方法。受到極大歡迎的一種方法是 Jakarta Velocity 模板包(請參閱?參考資料)。JSP 技術存在一個主要的不足:復雜的、功能豐富的應用程序傾向于需要極其復雜的 JSP 頁面,如果想使邏輯與表示分開,那么還需要進行額外的服務器工作來創建定制標簽。另一個不足就是 JSP 技術經常帶來了無法抑制的誘惑,將業務邏輯和表示混合,這容易導致需要繁重的維護工作的脆弱系統。

據我看來,JSP 技術常常是一個錯誤的選擇,而 Velocity(或者其他某種模板化方法)通常是正確的。但對于我們這個簡單例子,JSP 技術將起作用,可以說明我們需要介紹的概念。在這樣的簡單情況下,將一點點邏輯和一點點表示混合是可以接受的。但從專業的角度來說,多數情況下,這種做法是不明智的,即使許多編程人員都這樣做。

web.xml 文件

為了讓我們能夠使用將要創建的 JSP 頁面,我們必須告訴 Tomcat 如何處理該頁面。因此,我們必須在 WEB-INF 目錄中創建一個 web.xml 文件。如下所示:

<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'> <web-app><servlet><servlet-name>contacts</servlet-name><servlet-class>com.roywmiller.contacts.model2.ContactsServlet</servlet-class></servlet><servlet-mapping><servlet-name>contacts</servlet-name><url-pattern>/index.htm</url-pattern></servlet-mapping><servlet-mapping><servlet-name>contacts</servlet-name><url-pattern>*.perform</url-pattern></servlet-mapping><servlet><servlet-name>jspAssign</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class><init-param><param-name>logVerbosityLevel</param-name><param-value>WARNING</param-value></init-param><init-param><param-name>fork</param-name><param-value>false</param-value></init-param><load-on-startup>3</load-on-startup></servlet><servlet-mapping><servlet-name>jspAssign</servlet-name><url-pattern>/*.jsp</url-pattern></servlet-mapping> </web-app>

我們為?HelloWorldServlet?創建了一個基本的 web.xml 文件,但是它非常小。隨著應用程序變得更加復雜,web.xml 文件也不得不變得更智能。讓我們快速分析該文件。

<servlet>?標簽為 servlet 指定一個別名,我們將在該文件的別處使用它。它還告訴 Tomcat 實例化哪個類,以便在內存中創建 servlet。在我的 Eclipse 工作區中,我創建了?com.roywmiller.contacts.model2?包來保存該 servlet 類。無論需要什么,都可以調用我們的包,但是到 servlet 的路徑必須匹配?<servlet-class>?元素中的內容。我們定義的第二個 servlet 是下載 Tomcat 時附帶的,您不必修改它。它只是 JSP 正在處理的 servlet。

<servlet-mapping>?告訴 Tomcat 當某個 URL 到達服務器時,執行哪個 servlet。我們這里有三個映射。第一個將 Web 服務器查找的默認頁面(<index.htm>)映射到 servlet。第二個告訴 Tomcat 將以?.perform?結尾的 URL 映射到 servlet。該形式的 URL 將告訴 servlet 實現哪個動作(稍后,我們將更詳細地討論其工作方式)。第三個映射告訴 Tomcat 使用 JSP servlet 來處理 JSP 頁面。

JSP 頁面的用戶視圖

在我們的簡單例子中,我們不會花太多時間談論 JSP 技術。JSP 技術可以使事情簡單,不會陷入一般表示的細節中,特別是不會陷入 JSP 技術細節中。(有關的更多信息,請再次參閱?參考資料。)我們還會將所有事情放置在一個頁面上,即使這樣做有些不太現實。這將最大程度地減少僅僅為了說明如何使用 servlet 的重要概念而必須創建的頁面數。

我們的最初頁面將顯示合同列表,這將來自于一個包含了該列表的對象。它還將包含一個用于添加新合同的表單。該頁將如圖 5 所示。

圖 5. 合同列表頁面

雖然并非一件藝術作品,但該頁在頂部按照良好的格式顯示了所有合同。每一個頁面都有 Delete 鏈接,用戶可以單擊它來刪除特定的合同。該表單包含名稱和地址值字段,以及關于合同類型(我們的簡單示例中是 family 或 acquaintance)的單選按鈕。這個簡單頁面將允許我們探索如何在 servlet 應用程序使用簡單的動作框架。它還將讓我們探索如何在用戶會話期間使用請求,以及對 servlet 從瀏覽器接收的內容進行響應。

現在,我們準備創建該頁面。

JSP 頁面編碼

下面是我們的 JSP 頁面的代碼:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <%@ page import="java.util.*" %> <%@ page import="com.roywmiller.contacts.model.*" %> <html> <head> <title>Contacts List 1.0</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><style type="text/css">body, table, hr {color: black;background: silver;font-family: Verdana, sans-serif;font-size: x-small;} </style></head><body><jsp:useBean id="contacts" scope="session" class="com.roywmiller.contacts.model.ContactList"/><h2>Contact List 1.0</h2><hr size="2"/><table frame="below" width="100%"><tr><th align="left"></th><th align="left">Name</th><th align="left">Street</th><th align="left">City</th><th align="left">State</th><th align="left">Zip</th><th align="left">Type</th></tr><%List list = contacts.getContacts();for (Iterator i = list.iterator(); i.hasNext();) {Contact contact = (Contact)i.next();%><tr><td width="100"><a href="removeContactAction.perform?id=<%= contact.getId()%>">Delete</a></td> <td width="200"><%=contact.getFirstname()%> <%=contact.getLastname()%></td><td width="150"><%=contact.getStreet()%></td><td width="100"><%=contact.getCity()%></td><td width="100"><%=contact.getState()%></td><td width="100"><%=contact.getZip()%></td><td width="100"><%=contact.getType()%></td></tr><%}%> </table><br/><br/><br/><fieldset><legend><b>Add Contact</b></legend><form method="post" action="addContactAction.perform"><table><tr><td>First Name:<td><td><input type="text" size="30" name="firstname"></td></tr><tr><td>Last Name:<td><td><input type="text" size="30" name="lastname"></td></tr><tr><td>Street:<td><td><input type="text" size="30" name="street"></td></tr><tr><td>City:<td><td><input type="text" size="30" name="city"></td></tr><tr><td>State:<td><td><input type="text" size="30" name="state"></td></tr><tr><td>Zip:<td><td><input type="text" size="30" name="zip"></td></tr><tr><td>Type:<td><td><input type="radio" size="30" name="type" value="family">Family <input type="radio" size="30" name="type" value="acquaintance"checked> Acquaintance</td></tr></table><br/><input type="submit" name="addContact" value=" Add "></form></fieldset></body> </html>

此時,在該頁面上看到的大多數內容可能是希臘文。我們不會對所有內容都進行詳細討論,但是在接下來的幾屏中,我們將指出其中要點,便于您理解 servlet 將如何與該頁面進行交互。

簡單 JSP 頁面的剖析

在 JSP 頁面中,HTML 就是 HTML。Java 代碼嵌在該頁面中,如下所示:

<% Java code %>

為了在該頁面中嵌入 Java 代碼,必須告訴 JSP 頁面這些類位于何處,就像您在 Java 類中所做的那樣。可以用語句完成這項工作,如下所示:

<%@ page import="java.util.*" %>

我們的頁面顯示一個合同列表,該列表來自于一個?ContactList?實例,JSP 頁面通過下列代碼行可以了解該實例:

<jsp:useBean id="contacts" scope="session" class="com.roywmiller.contacts.model.ContactList"/>

這行代碼告訴 JSP 頁面在該頁面的別處使用一個名為?contacts?的 bean。它是?com.roywmiller.contacts.model.ContactList?的實例,并擁有?session?作用域。

請注意,該頁面的主體中有一個 Java?for?循環:

List list = contacts.getContacts(); for (Iterator i = list.iterator(); i.hasNext();) {Contact contact = (Contact)i.next(); %><tr><td width="100"><a href="removeContactAction.perform?id=<%= contact.getId()%>" >Delete</a></td><td width="200"><%=contact.getFirstname()%> <%=contact.getLastname()%></td><td width="150"><%=contact.getStreet()%></td><td width="100"><%=contact.getCity()%></td><td width="100"><%=contact.getState()%></td><td width="100"><%=contact.getZip()%></td><td width="100"><%=contact.getType()%></td></tr> <% }

這說明了 JSP 技術是如何混合 HTML 和 Java 語句的。在這里,我們將遍歷?contact?對象的合同列表。每進行一次循環,就要向 HTML 表添加一個?<tr>?元素。在該表中,每個合同為一行,我們調用?Contact?實例的 getter 來填充表單元。對于第一個單元,需要為每一行創建一個 Delete 鏈接。我們將?href?屬性設置為下列字符串:

removeContactAction.perform?id=<%= contact.getId()%>

當用戶單擊該鏈接時,先添加一個斜線(/),然后,該字符串將追加到發送至服務器的 URL 的末尾處。問號是請求參數的定界符(delimiter),緊跟在?name=value?對之后。本例中,我們發送每個合同的 ID。

該頁面別處也是發生相同的事情,例如,在表單中添加新合同。請注意?<form>?標簽:

<form method="post" action="addContactAction.perform">

當用戶單擊 Add 按鈕(表單底部的提交按鈕)時,addContactAction.perform?被追加到 URL 中。

這就是全部工作!一些精妙的語法正是許多專業編程人員要么吝嗇于使用 JSP 技術,要么創建各種 helper 類(例如定制的 JSP 標簽)來易化頁面的創建、讀取和維護的部分原因。但既然我們有了該頁面,就可以開始編寫一些代碼了。

創建 servlet

我們的 servlet 類似于?HelloWorldServlet,并添加了動作處理功能:

import java.io.IOException;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.roywmiller.contacts.actions.Action;public class ContactsServlet extends HttpServlet {protected ActionFactory factory = new ActionFactory();public ContactsServlet() {super();}protected String getActionName(HttpServletRequest request) {String path = request.getServletPath();return path.substring(1, path.lastIndexOf("."));}public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Action action = factory.create(getActionName(request));String url = action.perform(request, response);if (url != null)getServletContext().getRequestDispatcher(url).forward(request, response);} }

就像以前一樣,我們擴展?HttpServlet?并重載?service()?方法。在該方法中,我們:

  • 從導致調用 servlet 的 URL 中派生動作名。
  • 基于該名稱實例化正確的動作。
  • 告訴該動作開始執行。
  • 將響應發送給動作所指向的 URL。

我們從導致調用 servlet 的 URL 中派生動作名,而該 servlet 是從?request.servletPath()?獲得的。請記住,導致我們調用動作的所有 URL 都具有?*.perform?的形式。我們將解析該形式來獲得圓點左邊的字符串,該字符串就是動作名,然后將該動作名傳遞給?ActionFactory,以實例化正確的動作。現在,您看到我們為何告訴 Web 應用程序如何處理該形式的 URL,以及為何在 JSP 頁面中使用這些“神奇”的字符串。正是因為這樣,我們才可以在這里對它們進行解碼,并采取對我們有利的動作。有什么替代方案?大量的?if?語句和大量的附加代碼。正如我們將看到的,通過動作,需要執行的每個動作都已完全封裝。

這樣做很好,但是我們需要一些附加類來完成該任務。這就是動作框架要做的事。

簡單的動作框架

我們的簡單動作框架有 4 個主要組件:

  • ActionFactory。該工廠將請求中的動作名轉換成 servlet 可以用來完成其工作的動作類。
  • Action?接口。該接口定義所有動作的極其簡單的公共接口。
  • 名為?ContactsAction?的抽象類。該類實現了所有動作共用的一個方法,并強制子類實現另一個方法(perform())。
  • ContactsAction?的三個子類。這些子類使 servlet 能夠進行自我引導、添加新合同和刪除合同。

在 servlet 的?service()?方法中,該過程以?ActionFactory?開始。

ActionFactory

下面是我們的?ActionFactory:

import java.util.HashMap; import java.util.Map; import com.roywmiller.contacts.actions.Action; import com.roywmiller.contacts.actions.AddContactAction; import com.roywmiller.contacts.actions.BootstrapAction; import com.roywmiller.contacts.actions.RemoveContactAction;public class ActionFactory {protected Map map = defaultMap();public ActionFactory() {super();}public Action create(String actionName) {Class klass = (Class) map.get(actionName);if (klass == null)throw new RuntimeException(getClass() + " was unable to find an action named '" + actionName + "'.");Action actionInstance = null;try {actionInstance = (Action) klass.newInstance();} catch (Exception e) {e.printStackTrace();}return actionInstance;}protected Map defaultMap() {Map map = new HashMap();map.put("index", BootstrapAction.class);map.put("addContactAction", AddContactAction.class);map.put("removeContactAction", RemoveContactAction.class);return map;} }

ActionFactory?極其簡單。它有一個?Map?動作類及其名稱。我們在頁面中使用該名稱告訴 servlet 執行哪個動作。本例中,我們有三個動作:

  • BootstrapAction
  • AddContactAction
  • RemoveContactAction

記得要分別通過 Add 表單和 Delete 鏈接,將添加和刪除合同的動作作為 URL 發送給 servlet。BootstrapAction?僅僅適用于將?/index.htm調用至我們的動作框架中。

當告訴該工廠創建?Action?時,它將實例化該類,并把它送回實例。向該工廠添加新動作其實就是簡單地為該動作創建一個類,然后在工廠的動作?Map?中添加新的條目。

Action

Action?接口如下所示:

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public interface Action {public String perform(HttpServletRequest request, HttpServletResponse response);public void writeToResponseStream(HttpServletResponse response, String output); }

現在,我們將廣泛使用的方法是?perform()。而另一方法,writeToReponseStream()?允許動作直接寫入響應的輸出流,以傳遞給 JSP 頁面。寫入的任何內容(文本、HTML 等)都將在該頁面上顯示。我們暫時不需要使用該方法,但是,您可以在?ContactsAction?上獲得它,以查看它如何工作。記得我們在?HelloWorldServlet?里使用了該方法體中的代碼,因此,您不會對它感到陌生。

用 BootstrapAction 啟動

我們擁有的?ContactsAction?的最簡單的子類是?BootstrapAction,它是其他子類的一個好模型:

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class BootstrapAction extends ContactsAction {public String perform(HttpServletRequest request, HttpServletResponse response) {return "/" + "contactList.jsp";} }

我們僅實現?perform()?來完成需要的工作。本例中,我們只要返回一個指向 contactList.jsp 的 URL(開頭的斜線很重要,因此不要忘了它)。現在來回顧?ContactsServlet?上的?service()?方法:

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Action action = factory.create(getActionName(request));String url = action.perform(request, response);if (url != null)getServletContext().getRequestDispatcher(url).forward(request, response); }

該動作要么返回一個 URL 字符串,要么寫入輸出流,在 JSP 頁面上顯示。如果動作返回 URL 字符串,那么這是?BootstrapAction?的功勞,我們可以獲取?ServletContext,在 URL 上向它請求?RequestDispatcher,最后將請求和響應發送給 JSP servlet,以便構造該頁面。在這之后,只要動作 servlet 沒有寫入 JSP 頁面的?PrintStream(現在已經關閉),它就會取回控制權,并完成剩下的工作。

如果需要,可以在?response?上調用?sendRedirect(),而不是使用?RequestDispatcher:

response.sendRedirect("http://...");

但是,這樣做要付出代價。當使用調度程序時,我們將?request?和?response?授權給 JSP servlet,它還將轉發現有的?HttpSession。那樣就保存了該會話的內容。轉發給另一個 URL 就不會如此。此刻,當我們開始顯示該頁時,我們所關心的會話中沒有任何東西,因此,效果是一樣的。但是很快,保存會話內容就會很重要。

為了告訴動作框架這個新的可用動作,我們要向工廠的動作?Map?添加以下代碼行:

map.put("index", BootstrapAction.class);

添加合同

一個僅顯示頁面卻不允許您做任何事情的應用程序不是很有用。我們需要能夠添加合同的頁面。

為此,必須執行以下操作:

  • 創建一個名為?AddContactAction?的類。
  • 實現?perform(),向 Web 會話中維護的?ContactList?添加新的?Contact?實例。
  • 將新動作告訴工廠。

將動作告訴工廠就是在工廠的 map 中添加另一條目,正如我們用?BootstrapAction?所做的那樣。

AddContactAction?類及其已實現的?perform()?方法如下所示:

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;import com.roywmiller.contacts.model.Contact; import com.roywmiller.contacts.model.ContactList;public class AddContactAction extends ContactsAction {public String perform(HttpServletRequest request, HttpServletResponse response) {Contact newContact = createContact(request);HttpSession session = request.getSession();ContactList contacts = (ContactList) session.getAttribute("contacts");contacts.addContact(newContact);session.setAttribute("contacts", contacts);return "/contactList.jsp";}protected Contact createContact(HttpServletRequest request) {Contact contact = new Contact();contact.setFirstname(request.getParameter(RequestParameters.FIRSTNAME));contact.setLastname(request.getParameter(RequestParameters.LASTNAME));contact.setStreet(request.getParameter(RequestParameters.STREET));contact.setCity(request.getParameter(RequestParameters.CITY));contact.setState(request.getParameter(RequestParameters.STATE));contact.setZip(request.getParameter(RequestParameters.ZIP));contact.setType(request.getParameter(RequestParameters.TYPE));return contact;} }

在這里,我們要做的所有操作就是調用?createContact()?創建一個新的?Contact,并將其實例變量設置為包含請求參數的相應值。然后,我們在?HttpSession?中向?ContactList?添加新的?Contact。最后,我們告訴 servlet 返回 /contactList.jsp。

記住每當我們創建一個?Contact?時,構造函數就給它分配一個惟一的 ID。回顧一下 JSP 代碼。您將看到與我們在該動作中做什么有關的兩項重要事情。首先要注意的是,通過添加下列這個行,我們保證在會話中總是有一個?ContactList?實例:

<jsp:useBean id="contacts" scope="session" class="com.roywmiller.contacts.model.ContactList"/>

在第一次編譯并顯示(BootstrapAction?執行后發送至 JSP 頁面的結果)JSP 頁面時,它將實例化?ContactList。該對象沒有包含任何內容,因此,當我們啟動應用程序時,合同列表顯示為空。在?AddContactAction?中,我們修改該對象,添加新的合同信息,然后將它重新插入會話中。以后,當顯示該頁面時,它將讀取?Contact?實例的?ContactList?列表,并顯示它們。

第二,注意用來添加合同的表單,如下所示:

<form method="post" action="addContactAction.perform">table with labels and text input fields<input type="submit" name="addContact" value=" Add "> </form>

該表單的動作致使在請求中將?addContactAction.perform?發送給 servlet。然后,它提取?addContactAction?的一部分作為動作名,在工廠中查找它,并創建?AddContactsAction?的一個實例。

刪除合同

添加合同很重要,但是能夠刪除它們也同樣重要。能夠對合同進行編輯是很不錯,但本教程只能做到這些。此外,添加編輯功能將像添加另一動作一樣簡單。因此,我們暫時只是添加刪除合同的功能,然后轉向更有意思的事情。

與前面一樣,我們只要添加了一個新的動作類,并實現其?perform()?方法,告訴工廠添加了這個方法。我們還必須確保 JSP 代碼在合適的時候告訴 servlet 調用該動作。

可以在 JSP 頁面中查看合同表中每一行的?Delete?鏈接:

<a href="removeContactAction.perform?id=<%= contact.getId()%>" >Delete</a>

該鏈接告訴 servlet 請求工廠為名稱?removeContactAction?提供正確的動作類。它還傳遞了該請求中一個名為?id?的參數,該請求中有一個為當前合同 ID 設置的值。

我們的類如下所示:

import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;import com.roywmiller.contacts.model.ContactList;public class RemoveContactAction extends ContactsAction {public String perform(HttpServletRequest request, HttpServletResponse response) {int contactId = Integer.parseInt(request.getParameter("id"));HttpSession session = request.getSession();ContactList contacts = (ContactList) session.getAttribute("contacts");contacts.removeContact(contactId);session.setAttribute("contacts", contacts);return "/contactList.jsp";} }

這里,我們要做的就是從會話中提取?id?參數和?ContactList,告訴該列表刪除具有該?id?的?Contact,然后替換會話上的列表。最后但也是很重要的,我們要告訴 servlet 返回 contactList.jsp。

運行應用程序

如果好奇心還沒有占上風,那么您現在就應該運行這個應用程序,看看它是如何工作的。

啟動瀏覽器,并輸入下列 URL:

http://localhost:8080/contacts/

如果 Tomcat 運行正確,您就應查看 contactList.jsp,其列表中沒有合同。在 add 表單上的文本字段中輸入一些值,然后并單擊?Add?按鈕。您將在該列表中看到新的合同,該合同名稱的左邊有一個 Delete 鏈接。除非您修改它,否則其類型將設為 Acquaintance(單選鈕的默認類型選擇)。為了簡便起見,我們沒有對該表單進行任何驗證,因此,您可以輸入所有字段值完全相同的多個合同。每個合同都有一個惟一的 ID,因此,每個合同將分開顯示,您可以逐個刪除它們。

說得簡單點 —— 我們有了一個實用的 Web 應用程序!但我們無法保存合同列表,因此,每當啟動該應用程序時,我們都必須重新輸入它們。更糟的是,該應用程序的每位用戶都有相同的合同列表。我們是可以通過添加對于惟一用戶的支持,以及通過在文件中存儲數據(可以工作的最簡單的數據庫),來解決這些問題。在下一小節中,我們將完成這兩項工作。

用戶和數據

增強應用程序

在這一小節中,我們將對代碼和現有的 JSP 頁面進行少量重構(refactor),以便能為惟一的用戶處理持久存儲的合同數據。簡言之,我們進行下列工作:

  • 創建一個?ContactsUser?對象。
  • 為每個?ContactsUser?提供用戶名、密碼和合同列表。
  • 修改 JSP 頁面的?<jsp:useBean/>?標簽以使用?ContactsUser。
  • 添加 login.jsp 作為該應用程序的第一頁。
  • 修改 contactList.jsp,為登錄用戶提供友好的歡迎消息。
  • 向 contactList.jsp 添加 Logout 鏈接,然后調用?LogoutAction。
  • 添加 goodbye.jsp 來顯示個性化的再見消息。
  • 添加?LoginAction?和?LogoutAction。
  • 添加?UsersDatabase?來處理 usersDatabase.txt 中?Contacts?的存儲和檢索。
  • 通過重載 servlet 上的?init()?初始化?ContactsDatabase。
  • 重載 servlet 上的?destroy(),告訴?UsersDatabase?關閉 usersDatabase.txt。

實際上并非如此糟糕。惟一真正較新的概念是使用文件(只是更多標準 Java 語言工作)以及指向新頁面。所有動作處理機制都是相同的。這說明動作框架的功能強大,而創建該框架只需要花費一點點寶貴的時間。它完全不像 Jakarta 的 Struts 框架那樣復雜(請參閱?參考資料),將 Struts 框架用于應用程序中所進行的工作可能有點小題大做。

ContactsUser

刪除導入語句和存取程序之后,ContactsUser?對象將如下所示(您可以在?contacts.jar?中找到完整的源代碼):

public class ContactsUser {protected String username = "";protected String password = "";protected List contactList = new ArrayList();public ContactsUser() {}public ContactsUser(String username, String password, List contactList) {this.username = username;this.password = password;this.contactList.addAll(contactList);}public boolean hasContacts() {return !contactList.isEmpty();}public void addContact(Contact aContact) {contactList.add(aContact);}public void removeContact(Contact aContact) {contactList.remove(aContact);}public void removeContact(int id) {Contact toRemove = findContact(id);contactList.remove(toRemove);}protected Contact findContact(int id) {Contact found = null;Iterator iterator = contactList.iterator();while (iterator.hasNext()) {Contact current = (Contact) iterator.next();if (current.getId() == id)found = current;}return found;}accessors... }

該類保存應用程序用戶的有關信息。這通常就是它所要做的所有工作。它保存用戶的用戶名和密碼,并維護該用戶的合同列表。它允許動作框架中的各種動作為該用戶添加和刪除?Contact。在這里,不帶參數的構造函數被用于單元測試。另一個接收三個參數的構造函數才是應用程序用戶所使用的。

您可能會問自己,“該類為何沒有一個?ContactList?實例變量呢?”畢竟,我們早先花功夫創建了這樣一個實例。我們為何不使用它呢?答案很簡單,我們實際上不再需要該類。它包裝了一個?ArrayList,并為我們提供了一些輔助方法。這些輔助方法實際在?ContactUser?上更有意義。如果我們使用了?ContactList,則需要通過擁有相同名稱以及需要完成相同事情的?ContactUser?來調用它上面的方法。例如,如果?ContactUser?擁有一個?ContactList,并且將該實例變量命名為?contactList,那么?addContact()?將如下所示:

public void addContact(Contact aContact) {contactList.addContact(aContact); }

在這里對其他對象進行授權有些愚蠢。因此,我們刪除了?ContactList?類。那正是重構要做的全部工作。我們簡化了代碼,并減少了系統中類的數目,但仍能完成相同的任務。擁有?ContactList?是創建系統時的中間步驟。它允許我們啟動并運行系統,并幫助我們創建動作框架。然后,它的有效壽命就結束了,我們將刪除它。編寫一些代碼并不代表您必須永遠維護它們。

修改 contactList.jsp

修改 JSP 頁面來使用新的?ContactUser?十分簡單。我們需要進行三處修改。

第一處就是修改?<jsp:useBean>?標簽,如下所示:

<jsp:useBean id="user" scope="session" class="com.roywmiller.contacts.model.ContactsUser"/>

現在,頁面將實例化?ContactsUser,而非?ContactList。

第二處修改就是更新頁面中的表行構建邏輯,以使用新的?user?變量:

<%List list = user.getContacts();for (Iterator i = list.iterator(); i.hasNext();) {Contact contact = (Contact)i.next(); %>

第三處修改就是為用戶添加一個退出鏈接:

<a href="logoutAction.perform">Logout</a>

我們將該鏈接置于“Contacts 1.0”頭旁邊。當用戶單擊該鏈接時,servlet 將執行?LogoutAction。

添加登錄/退出頁面

與其他頁面相比,支持登錄和退出的頁面都十分簡單。惟一的差別存在于?<body>?標簽中。下面是 login.jsp:

<body> <h2>Contact List 1.0</h2> <hr size="2"/> <fieldset> <legend><b>Please Login</b></legend> <form method="post" action="loginAction.perform"><table><tr><td>Username:<td><td><input type="text" size="30" name="username"></td></tr><tr><td>Password:<td><td><input type="text" size="30" name="password"></td></tr></table><br/><input type="submit" name="login" value=" Login "> </form> </fieldset></body>

該頁面有一個表單,其中帶有兩個文本字段和一個提交按鈕。當用戶單擊?Login?時,servlet 將執行?LoginAction。

下面是 goodbye.jsp:

<body> <jsp:useBean id="user" scope="session" class="com.roywmiller.contacts.model.ContactsUser"/><h2>Contact List 1.0</h2> <hr size="2"/> Goodbye <%= user.getUsername() %>! </body>

該頁面調用?ContactsUser?bean 上的?getUsername()?來顯示個性化的再見消息。

當用戶用一個數據庫中沒有的用戶名嘗試登錄時,應用程序將放棄登錄,并將用戶指向一個錯誤頁面,如下所示:

<body> <h2>Contact List 1.0</h2> <hr size="2"/> <fieldset> <legend><b>Error</b></legend> There was an error: <%= session.getAttribute("errorMessage") %> </fieldset> </body>

這是我們擁有的最簡單的頁面。它使用可從所有 JSP 頁面獲得的默認?session?變量來顯示出錯消息。

添加 LoginAction

LoginAction?類如下所示:

public class LoginAction implements Action {public String perform(HttpServletRequest request, HttpServletResponse response) {String username = request.getParameter(USERNAME);String password = request.getParameter(PASSWORD);ContactsUser user = UserDatabase.getSingleton().get(username, password);if (user != null) {ContactsUser contactsUser = (ContactsUser) user;request.getSession().setAttribute("user", contactsUser);return "/contactList.jsp";} elserequest.getSession().setAttribute("errorMessage", "Invalid username/password.");return "/error.jsp";}}

該動作從請求中提取 username 和 password 參數,然后用 username/password 組合查看數據庫中是否包含該用戶。如果存在該用戶,那么就將該用戶置于會話中,并直接進入 contactList.jsp。如果數據庫中沒有該用戶,那么就在會話上設置一條出錯消息,并轉至 error.jsp。

現在,添加動作對于我們而言應該很容易了。我們向動作工廠添加一個條目,如下所示:

map.put("loginAction", LoginAction.class);

在設置好頁面之后,工廠會感知新動作,添加操作也就完成了。您應該能夠運行該應用程序,并看到登錄頁面。當輸入用戶名和密碼時,不管輸入的是什么,您都會看到出錯頁面。等一會兒之后,您就可以通過有效的用戶名和密碼登錄,并看到包含空合同列表的 contactList.jsp。

添加 LogoutAction

LogoutAction?類如下所示:

public class LogoutAction implements Action {public String perform(HttpServletRequest request, HttpServletResponse response) {UserDatabase.getSingleton().shutDown();return "/goodbye.jsp";}}

在這里,我們將告訴數據庫執行?shutDown()?操作。UserDatabase?上的方法如下所示:

public void shutDown() {writeUsers(); }protected void writeUsers() {StringBuffer buffer = new StringBuffer();Collection allUsers = users.values();Iterator iterator = allUsers.iterator();while (iterator.hasNext()) {ContactsUser each = (ContactsUser) iterator.next();UserRecord record = new UserRecord(each);buffer.append(record.getFullRecord());}writeText(buffer.toString()); }protected synchronized void writeText(String text) {Writer writer = null;try {writer = new FileWriter(usersFile.getAbsolutePath());writer.write(text);} catch (Exception e) {throw new RuntimeException("Unable to append to file.", e);} finally {closeWriter(writer);} }

shutDown()?調用?writeUsers(),該方法將迭代內存中保存的所有用戶(當 servlet 對自身進行初始化時,將從我們讀入該文件的地方開始),為每個用戶創建一個?UserRecord,然后將完整的字符串傳遞給?writeText()。writeText() 將該字符串寫入文件中,重寫現有的內容。UserRecord?類是一個極好的輔助類,封裝了文件中每條用戶記錄的所有煩雜的標記工作。您可以自己檢查代碼(關于完整的源代碼清單,請參閱?contacts.jar)。

一旦關閉數據庫,就可以告訴 servlet 發送 goodbye.jsp,顯示個性化的再見。

userDatabase.txt 文件

大多數 Web 應用程序從某種“數據庫”中訪問數據。許多都使用行業級(industrial-strength)的 RDBMS,但文本文件也可以是數據庫。它是可以工作的最簡單的數據庫。如果您將它包裝得很好,并將訪問細節隱藏在一個接口之后,而該接口使得應用程序中的其他類極易于訪問這些數據,那么底層數據采用什么樣的存儲形式實際上就沒什么關系。

在這個應用程序中,我們將使用一個文本文件。該文件將按照下列形式,為每位用戶保存一行:

username password comma-delimited contact1 info|comma-delimited contactN info|...

該文件中的用戶名將是明文,但出于安全考慮,密碼將是 Base64 編碼(絕對最簡單)。合同條目將用逗號分隔。而合同本身將通過?|?字符分隔。這種格式沒有什么特別。它只是執行我們需要它完成的工作,以允許我們易于解析該文件。

為了方便,我們將該文件放置在本項目的根目錄中,以便該文件的路徑簡單直接。

為了使事情簡單,該應用程序不支持用戶維護功能,這意味著無法在應用程序中添加或刪除用戶。這就表示您必須手工將用戶添加到 userDatabase.txt 中。例如,要添加一個名為?testuser?以及密碼為?password?的用戶,就要向該文件添加下列一行:

testuser cGFzc3dvcmQ=

每個條目中的密碼都是通過 Base64 編碼進行編碼的。您可以在?contacts.jar?中使用?EncoderDecoder?類來計算您密碼的編碼版本。它的?main()?方法允許您輸入明文字符串,然后運行該類,在控制臺上輸出已編碼的密碼。

UserDatabase

UserDatabase?包裝了與文本文件的交互。這個類的清單看上去很大,但是并不復雜(大部分讓人感覺很復雜的東西是那些額外的 Java 編碼內容,處理讀寫文件操作需要它們)。我們將在本面板上討論一些要點(關于完整的代碼清單,請參閱?contacts.jar)。

該類實現了 Singleton 模式,并且維護了一個實例,而所有用戶則通過調用?getSingleton()?共享這個實例。

該類維護了?ContactsUser?的一個?Map,該 Map 將用戶名與密碼的組合作為每個條目的密鑰。任何東西都可以充當每個條目的鍵,但這個比較方便。

在 servlet 的?init()?方法中,我們將告訴?UserDatabase?數據庫文件位于何處(基于?ServletContext),然后告訴它通過調用?initialize()?初始化它本身。該方法如下所示:

public void initialize() {usersFile = new File(databaseFilePathname);String allUsers = retrieveText();StringTokenizer tokenizer = new StringTokenizer(allUsers, "\n");while (tokenizer.hasMoreTokens()) {String userEntry = tokenizer.nextToken();UserRecord record = new UserRecord(userEntry);put(new ContactsUser(record.getName(), record.getPassword(), record.getContactList()));} }

該方法通過調用?retrieveText?讀入完整的文件,標記較大的字符串,為每個用戶創建?UserRecord,然后調用?put()?來在該 map 中放置新的?ContactsUser。該方法的真正作用體現在調用?retrieveText()?和?put()?中:

protected synchronized String retrieveText() {BufferedReader bufferedReader = null;try {bufferedReader = new BufferedReader(new FileReader(usersFile.getAbsolutePath()));char charBuff[] = new char[(int) new File(usersFile.getAbsolutePath()).length()];bufferedReader.read(charBuff);return new String(charBuff);} catch (Exception e) {throw new RuntimeException("Unable to read in the file.", e);} finally {closeReader(bufferedReader);} }protected void closeReader(BufferedReader bufferedReader) {try {if (bufferedReader != null)bufferedReader.close();} catch (Exception ex) {} }public void put(ContactsUser user) {String userKey = user.getUsername() + user.getPassword();users.put(userKey, user); }

retrieveText()?方法負責完成文件讀取工作。它創建了一個?BufferedReader,將整個文件內容讀入到字符緩沖區中,然后將這些內容轉換成一個?String。在其?finally?子句中,它只調用?closeReader()?來完成該工作。writeText()?方法將輸出寫入文件,重寫現有的內容。該方法隱藏了同一類型的文件交互細節。

put()?方法為用戶創建密鑰(用戶名加上密碼),并將該密鑰插入用戶的 map 中。

有效地使用 servlet

簡介

本教程中,我們只涉及了用 servlet 可完成的淺層功能。Web 應用程序可以與您所能想像的一樣復雜。盡管所有 Web 應用程序的底層機制基本上是相同的,如果用 Java 語言編寫代碼,servlet 將是核心部分。創建更為復雜的應用程序實質上就是使用更復雜的工具和庫。

然而,許多編程人員會在這個地方犯錯,從而導致創建出糟糕的 Web 應用程序。這一小節包含一些關于如何避免這些錯誤的建議。大多數具有 Web 開發經驗的 Java 編程人員都贊同其中的一些建議。還有一些更具爭議性的建議。無論在哪種情況下,它們都將幫助您較好地了解 servlet。

使用一個 servlet

如果您不能只使用一個 servlet,就使用盡可能少的數目。實際上,我建議您只使用一個,直到肯定無法再這樣下去。應該不需很多 servlet,您肯定無需為每種類型的請求提供一個 servlet。

不要在 servlet 上耗費太長時間

在 servlet 中盡可能少花時間。

servlet 不是為業務邏輯提供的場所。只有糟糕的 OOP 設計才那樣。將 servlet 考慮成以下兩種事物之一:

  • UI 后面的一個附加層,幫助服務器了解“事件”。
  • 服務器前面的一個附加層,允許您將瀏覽器用作 UI。

無論將 servlet 視為哪一種事物,它都是您快速將問題分派到應用程序的其他部分、然后退出的一個地方。

使用動作

動作框架,即使是像本教程中所使用的一樣簡單,是一種功能強大的方法。它允許您采納前面的意見:在 servlet 中花費盡可能少的時間。它還是很好的 OOP 設計。每個動作類完成一件事,或一組聯系緊密的事情。

某些人認為這會分裂代碼,使之更難以理解。我認為這種反對源于以下兩件事:

  • 人們不習慣于查看面向對象的代碼,當您第一次開始查看它時,肯定會感覺它們支離破碎的。
  • 人們更喜歡程序代碼,即使該代碼在對象中。

考慮其他選擇。若沒有動作(假設您不是簡單地授權給其他對象,不調用“動作”),則必須使用大量的?if?語句。如果只有兩三個 if 語句,代碼可能還很容易閱讀。但是如果您有 10 個 if 語句,則很可能令人感到目眩。要求您滾動多個屏幕的方法比較令人討厭。它通常意味著該方法要進行太多工作。至少,您要將?service()(或?doGet()?等)中完成的類似于動作的事情提取到其他方法中,要對這些方法起個好的名稱,這樣您才知道它們是做什么的。

使用動作。在需要添加功能時,盡管添加動作好了。

使用 service(),除非您不能使用它

servlet 世界中有許多人們聲稱,您不應重載?service()。并且說您應該重載?doGet()?和/或?doPost(),可以讓它們相互調用,使代碼類似于下面所示:

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {statements }public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response); }

我不知道這些思想源于何處。Bruce Eckle 指出該思想是從 CGI 時期遺留下來的,當人們開始習慣注意進來的是?GET?還是?POST?時。(關于對他的理論的更詳細版本的鏈接,請參閱?參考資料。)我從未聽到不要使用?service()?的好理由。任何情況下,如果您使用 service(),然后確定它將比使用任何一種?doX()?風格都要好,那么請重構代碼!在這個時候,就使用?service(),因為它更簡單。

不要混合表示和業務邏輯

對于簡單的應用程序而言,生成復雜的 HTML 字符串來輸出 JSP 頁面的輸出流很不錯,但是創建功能更豐富的應用程序時,該方法要困難得多。

將表示放置在它所屬的地方是一種比較明智的做法:在頁面中。JSP 技術允許您完成該工作,但正如我前面所說的,它需要做大量工作才能使業務邏輯與表示分離。諸如 Velocity 之類的模板化引擎通常是更好的選擇。無論您選擇哪種方法,都要盡可能少地混合業務邏輯和表示。

很好地處理異常

我們沒有在本教程中過多談論這一點,但是異常處理在創建 Web 應用程序時變得很重要。沒有比在服務器端發生一些意想不到的事情,然后在瀏覽器中看到隱藏的堆棧跟蹤更讓用戶沮喪的了。其中一些跟蹤可能極其遲鈍、晦澀。追查它們可能令人很苦惱。

許多打包的 Web 應用程序開發庫,如 Struts(請參閱?參考資料),都附帶了用來處理動態消息(包括錯誤)顯示的內置框架。您可以使用這些功能。

不要使用每種功能

您是否需要使用您正采用的 Web 應用程序開發框架或庫中的每項功能呢?很可能不需要,每一種功能都使用將使代碼比您所需要的要復雜得多。實際上,除了可以工作的最簡單框架,我建議您根本不要使用別的框架。有時候您知道,對于您手頭的問題而言,將要使用或需要使用的功能或框架可能有些小題大做。

當使用框架是可取的方法時,就使用它。不要假定您需要它,等待系統告訴您需要使用時再使用它。一些編程人員認為那是“糟糕的設計”。并非如此。假設您將需要一個特定的框架,甚至是需要該框架中的某個特定功能,那么這樣的設計可能是過分設計。您應針對所需要的進行設計;設計通常會隨著系統擴展而更改。在開發開始之前選擇框架是無益的,如果該框架不支持或不允許完成您需要完成的一些工作,那會讓您急得撞墻。

結束語

本教程中,您知道了 Java servlet,以及如何專業地使用它們。當然,本教程中的例子十分簡單,但是它們說明了創建 Web 應用程序將用到的大部分 servlet 概念。還有更多可用的功能(配置等),但是幾乎用 Java 語言編寫的每個 Web 應用程序的核心都是一個或多個 servlet,它們在幕后的一臺或多臺服務器上充當業務邏輯的網守。

更重要的是,您了解了很好地使用 servlet 的一些技術。Web 應用程序常常會變成一大堆凌亂的代碼。通過使用由基本 OOP 原則驅動的簡單技術,您可以避免這種混亂,創建易于增強和維護的應用程序。

參考資料

    • 下載本教程所附帶的?contacts.jar。
    • 從?Apache Jakarta Project?下載?Tomcat。
    • 您還需要來自于 Sysdeo 的用于 Eclipse 的 Tomcat 插件。您一旦安裝了該插件,就需要檢查 Sysdeo 的?HelloWorld servlet 設置“教程”。
    • David Geary 撰寫的?Advanced JavaServer Pages(Pearson Higher Education,2001)是您必讀的一本參考書。
    • 如果您的 Web 應用程序對于簡單的 JSP 組件來說太復雜,那么您可能希望研究?Velocity Template Engine?或?Struts Web Application Framework。這兩個項目都由 Apache Foundation 主持。
    • Java Technology 主頁是“官方的”Java 語言資源。該站點的?Java Servlet 技術專區提供了關于用 servlet 進行編程的豐富信息。
    • Sun 的?The Java tutorial?是一本優秀的參考資料。其中有對 servlet 的描述。
    • developerWorks New to Java technology page是為剛入門 Java 開發人員提供的 developerWorks 資源交換中心,其中包括了教程和認證資源的鏈接。
    • developerWorks Technology 文檔庫包含了到 developerWorks Java 學習資料的鏈接,包括諸如這篇和 Java 文章等附加教程。
    • Bruce Eckle?思考了許多 Web 編程人員不愿重載?service()?的歷史原因。
    • 在 developerWorks?Java 技術專區中,可以找到關于 Java 編程的各個方面的文章。
    • 請訪問?Developer Bookstore,獲得技術書籍的完整清單,其中包括大量?Java 相關書籍。
    • 關于?developerWorks?上免費的 Java 相關教程的完整清單,還請查看?Java 技術專區教程頁面。
  • from:?https://www.ibm.com/developerworks/cn/education/java/j-intserv/j-intserv.html

總結

以上是生活随笔為你收集整理的Java Servlet 技术简介的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美人与禽zozzozzo | 欧美日韩国产精品一区 | 亚洲高清中文字幕 | 国产精品午夜无码专区 | 久久er99热精品一区二区 | 人人干夜夜操 | 五月天天 | 国产日韩精品一区 | 国产精品一线二线 | 久久久久中文字幕亚洲精品 | 亚洲第一精品在线观看 | av私库在线观看 | 拍国产真实乱人偷精品 | 青青草视频在线免费观看 | 男受被做哭激烈娇喘gv视频 | 床戏高潮做进去大尺度视频网站 | 人禽高h交 | 极品美女开粉嫩精品 | 在线碰 | 色版视频| 国产极品美女在线 | 国产一区二区三区视频免费观看 | 日韩av.com| 看黄色的网站 | 少妇大叫太粗太大爽一区二区 | 日本三级中文字幕 | 国产av一区不卡 | 中国一级特黄毛片 | 天堂亚洲 | 国产农村妇女毛片精品 | 97热久久| 国产精品无码中文字幕 | 精品少妇一二三区 | 自拍视频一区二区 | 性欧美熟妇videofreesex | av手机| 国产精品一区二区三区四区在线观看 | 日本福利视频导航 | 亚洲av久久久噜噜噜熟女软件 | 欧美日韩电影一区 | 亚色一区 | 精品一区二区三区四区五区六区 | 国产精品天美传媒沈樵 | 在线日韩一区二区 | 麻豆福利在线 | www噜噜噜 | 日本a级片网站 | 久久精品6 | 久久成人黄色 | 成人视品 | 亚洲一区二区三区高清视频 | 亚洲精品免费网站 | 秋霞影院午夜丰满少妇在线视频 | 熟女一区二区三区视频 | 牛人盗摄一区二区三区视频 | 91精品国产综合久久福利 | 精品国产欧美 | 精品伦精品一区二区三区视频密桃 | 成人福利影院 | 一级黄色a级片 | 人妖性做爰aaaa | 亚洲爱爱片 | 国产婷婷在线观看 | 91爱在线观看 | 国产在线视频第一页 | 一眉道姑 | 九色精品视频 | av在线网址观看 | 欧美日韩在线观看一区二区三区 | 国产成年人免费视频 | 久久手机看片 | 日日摸日日干 | 国产色91| 免费看污黄网站在线观看 | 久久久亚洲天堂 | 狠狠婷| 亚洲精选一区二区三区 | 亚洲久久色 | 精品久久电影 | www性欧美 | 香港三级日本三级韩国三级 | 色婷婷视频在线 | 中国老太婆性视频 | 天天cao| 成人精品免费网站 | 人人妻人人澡人人爽人人精品 | 桃色激情网 | 欧美狂猛xxxxx乱大交3 | 欧美a级在线免费观看 | 精品少妇一区二区 | 欧美成人精品一区二区 | 开心激情五月网 | 最新国产露脸在线观看 | 国产成人精品亚洲日本在线观看 | 欧美成人做爰猛烈床戏 | 性淫影院| 亚洲青青操| 精品一区三区 | www.操.com |