程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 走上開放之路: .NET開發人員的J2EE基礎(下)

走上開放之路: .NET開發人員的J2EE基礎(下)

編輯:關於JAVA

相關文章:走上開放之路: .NET 開發人員的 J2EE 基礎(上)

.NET 和 J2EE 應用模型:理解它們的區別

要理解 J2EE 應用程序體系結構的基本概念,第一步是確定如何將現有的 ASP.NET 應用 程序移植到一個基於 J2EE 的模型,或者確定如何從頭編寫一個 J2EE 應用程序。我們將考 察幾個 ASP.NET 模型,以及可能如何將它們轉換為根據 J2EE 組件構建的模型。您將看到簡 單的“意大利面條式的代碼”如何演進為一個更優雅、可復用和可擴展的環境。在研究一些 代碼例子之前,讓我們首先進一步考察一下 Web 應用程序中使用得最多的兩個 J2EE 組件: Java Servlets 和 JavaServer Pages 技術。

Java Servlets 編程:基礎

編寫 Java Servlets 是為了以編程方式控制來自浏覽器的 URL 請求。典型的 servlet 調用類似如下:

客戶端向 Web 服務器發出請求,同時指定一個 servlet 作為 URL 的一部分 &mdash;&mdash;例如: <FORM action="/myWebApp/LoginServlet" method="POST"> 。

Web 服務器將該請求轉發給應用服務器,後者查找 servlet 類實例的位置。

然後 Web 容器調用該 servlet。(該 servlet 的單個實例將加載到內存中,每個請求在 不同的線程中調用該單個實例。)

注意 HTML 表單中 servlet 的 URL包括該 servlet 的名稱和一個稱為 上下文根 (context root) 的前綴(在上面的例子中是 /myWebApp )。上下文根大致等價於一個 IIS 虛擬目錄。

圖 4 描述了這些步驟。

圖 4. Servlet 調用

Servlet 擴展了 HttpServlet 類。您可以根據需要重載 HttpServlet 的以下方法:

init() :在應用服務器加載某個 servlet 時調用

destroy() :在應用服務器卸載某個 servlet 時調用

doGet() :在 servlet 通過 HTTP GET 被調用時調用

doPost() :在 servlet 通過 HTTP POST 被調用時調用

Servlet 的編寫涉及編寫代碼來處理 HTTP 請求、處理任何參數,以及直接返回 HTML 內 容或委托其他資源(比如 JSP 頁面)來處理響應。不推薦從 servlet 直接返回 HTML 內容 ,因為在 Java 類中管理 HTML 代碼很麻煩。

當應用服務器調用 doGet() 或 doPost() 方法時,將有兩個對象作為參數被傳遞:

HttpServletRequest 允許訪問任何請求參數和導致產生該 servlet 調用的 HTTP 請求中 的其他信息。

HttpServletResponse 充當返回客戶端的通信渠道,允許 servlet 直接返回內容或返回 其他 HTTP 返回代碼(錯誤、重定向,等等)。

清單 20 和 21 顯示了兩個 Hello World servlet。清單 20 直接返回內容,而清單 21 使用 JSP 頁面來返回內容。

清單 20. HelloWorld servlet: 直接返回內容

public class HelloWorldServlet extends HttpServlet {
/**
  * Handles all HTTP POST requests
  *
  * @param request Object that encapsulates the request to the servlet
  * @param response Object that encapsulates the response from the servlet
  */
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
  PrintWriter out = response.getWriter();
  out.println("<html>");
  out.println("Hello World");
  out.println("</html>");
} catch (Throwable t) {
  ...
}
}
}

注意您從響應對象中得到了一個 PrintWriter ,並將 HTML 一次一行地輸出到客戶端。

清單 21. HelloWorld servlet: 使用 JSP 頁面來返回內容

public class HelloWorldServlet extends HttpServlet {
/**
  * Handles all HTTP POST requests
  *
  * @param request Object that encapsulates the request to the servlet
  * @param response Object that encapsulates the response from the servlet
  */
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
       RequestDispatcher rd = getServletContext().getRequestDispatcher ("helloworld.jsp");
       rd.forward(request, response);
} catch (Throwable t) {
  ...
}
}
}

RequestDispatcher 是您想要向其轉發請求的資源的包裝器。注意要同時包括原始請求和 響應對象,以便目標資源能夠訪問它們。從 getServletContext() 返回的 ServletContext 允許 servlet 與底層應用服務器通信,以獲得一個 RequestDispatcher 。 注意所有 servlet 都能夠通過 getServletContext() 方法訪問它們的 ServletContext 。

使用 JavaServer Pages 技術來編程:基礎

JSP 技術使您能夠使用 Java 語言進行服務器端編程。JSP 頁面是包含 HTML 和 Java 代 碼的復合頁面,其中的 Java 代碼由應用服務器在將頁面返回客戶端之前處理。應用服務器 處理該嵌入代碼,以便在將頁面返回客戶端之前生成靜態內容。像 .aspx 文件一樣,JSP 文 件通常看起來像具有一些附加標簽和 Java 代碼片斷的 HTML。

J2EE 應用服務器在 JSP 頁面第一次被請求時,將每個JSP 頁面轉換為一個特殊的 servlet。該 servlet 將編譯並加載到內存中。只要該 JSP 源代碼沒有修改,它就一直為針 對該頁面的請求提供服務。當源代碼修改時,該過程又重來一次,這樣就產生了一個新版本 的 servlet。

可以在 JSP 頁面中使用幾個特殊的 JSP 標簽,用戶還可以定義他們自己開發的標簽的行 為。這些自定義的標簽大致等價於 ASP.NET 中的自定義組件。還可以向 JSP 頁面的不同部 分添加一些 Java 代碼塊。J2EE 運行時環境使得許多變量(稱為 隱含變量)對您的這些 Java 代碼片斷可用。隱含變量的例子包括:

request:與頁面的特定調用相關聯的 HttpServletRequest

response:與頁面的特定調用相關聯的 HttpServletResponse

out:與 HttpServletResponse 相關聯的 PrintWriter

清單 22 顯示了 JSP 頁面的一個例子,其中包含 HTML 和 Java 代碼的混合。 <%和 %>標簽之間的 Java 代碼部分稱為 scriptlet。

清單 22. HelloWorld servlet:使用 JSP 頁面來返回內容

<html>
<title>JSP page example</title>
The date is:
<%
Date date = new Date();
out.println(date);
%>
Some more HTML
The value of <em>e</em> is:
<%
double e = Math.exp(1.0);
out.println(e);
%>
Yet even more HTML
The value of PI is:
<%
double pi = 22.0/7.0;
out.println(pi);
%>
</html>

注意其中使用了 JSP 隱含變量 out 來將內容寫回客戶端,還要注意 HTML 和 Java 代碼 的交織。

一個例子場景

為了說明各種體系結構選項,我們將使用一個簡單的用戶登錄場景,它包含:

一個具有 HTML 表單的登錄頁面,它從用戶那裡收集用戶名和密碼

驗證用戶憑據(可能使用數據庫)並重定向站點主頁的應用邏輯

這個簡單的例子允許我們描述各種各樣的應用體系結構。

“意大利面條式”代碼

在 ASP.NET 方,“意大利面條式”代碼方法使用單個 .aspx 文件來同時包含應用邏輯和 HTML 表單。不存在代碼分離(code-behind)。(不推薦對現實中的例子采用這種方法,因 為這樣所有表示邏輯和應用邏輯都將是單個文件,從而阻止了您復用用於驗證用戶憑據的代 碼。)該代碼的輪廓看起來類似清單 23 中所示的代碼。

清單 23. ASP.NET 中的“意大利面條式”代碼

<html>
<head>
<title>Login example</title>
<script language="C#" runat=server>
...
private void btnLogin_Click(object sender, System.EventArgs e)
{
   // Get the form field values
   ...
   // Validate the username and password
   if (ValidateUser(username, password) {
    Response.Redirect("mainpage.aspx");
   }
}
private bool ValidateUser(string userName, string password) {
    ...
}
...
</script>
</head>
<body>
   <form runat="server">
   <!-- login form fields go here -->
   </form>
</body>
</html>

正如清單 24 所示,您可以在 J2EE 中采用相同的方法,使用單個 JSP 頁面來同時包含 表單和應用邏輯。

清單 24. J2EE 中的“意大利面條式”代碼

<html>
<head>
<title>Login example</title>
<%!
private boolean validateUser(String userName, String password) {
    ...
}
%>
...
</head>
<body>
<%
   if (request.getMethod().equals("GET") ) { %>
   <form method="POST" target="login.jsp">
   <!-- login form fields go here -->
   </form> <% }
   else {
     String userName = request.getParameter("username");
     String password = request.getParameter("password");
      if (validateUser(userName, password)) {
       response.sendRedirect("mainpage.jsp");
     }
     ...
  } %>
</body>
</html>

JSP 模型不是事件驅動的,因此您需要檢查表單是否被發送回去了,方法是檢查該請求, 並在它不是 POST 請求時包括表單的 HTML。如果它是 POST 請求,您將使用 JSP 中聲明的 一個方法來驗證登錄。注意 <%! 的使用表明該代碼是一個方法。Scriptlet 代碼將在 JSP 頁面處理期間遇到它們的時候執行,因此用於 scriptlet 的標簽( <% %> )對 方法無效。還要注意如何使用 if/then/else 編程結構來容易地包括或排斥較大的 HTML 塊 。與在 ASP.NET 例子中一樣,不推薦將此方法用於 J2EE 開發。表示代碼(HTML)和應用邏 輯的混合僅允許很少的復用,並且使得代碼難於維護。

改進的“意大利面條式”代碼

在 ASP.NET 方,一種更好的方法建立在前一個例子基礎上,不過除了 .aspx 文件外,它 還使用了代碼分離文件。事件處理代碼和用戶驗證代碼轉移到了一個代碼分離文件中,原先 的 .aspx 文件只剩下 HTML 表單和其他 HTML 元素。這相對於前一種方法來說當然是一種進 步;表示代碼更清楚地分離了,這樣可以讓一個 HTML 設計師負責表示,讓一個程序員負責 代碼分離文件。

如果使用標准 J2EE 組件,您就無法使用 ASP.NET 的事件驅動的代碼分離文件方法。 J2EE 端的一種更好方法是將應用邏輯轉移到一個 Java Servlet,從而使 JSP 頁面僅限於使 用 HTML 組件。就像 ASP.NET 中的代碼分離文件方法相對於“意大利面條式”代碼是一種改 進一樣,這同樣也是一種改進。清單 25 顯示了如何通過把應用邏輯放到 servlet 中來簡化 JSP 頁面。

清單 25. J2EE 中改進的“意大利面條式”代碼 JSP page

<html>
<head>
<title>Login example</title>
...
</head>
<body>
<form method="POST" target="LoginServlet">
<!-- login form fields go here -->
</form>
</body>
</html>

清單 26 顯示了 servlet 中的驗證代碼和導航邏輯。

清單 26. J2EE 中改進的“意大利面條式”代碼 Java Servlet

public class LoginServlet extends HttpServlet {
/**
  * Handles all HTTP POST requests
  *
  * @param request Object that encapsulates the request to the servlet
  * @param response Object that encapsulates the response from the servlet
  */
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
      String userName = request.getParameter("username");
      String password = request.getParameter("password");
      if (validateUser(userName, password)) {
       response.sendRedirect("mainpage.jsp");
      }
       ...
} catch (Throwable t) {
  ...
}
}
private boolean validateUser(String userName, String password) {
    ...
   }
}

清單 26 中的 servlet 是表單提交的目標,並且充當一個控制器――處理用戶輸入並基 於該輸入調出適當的頁面。注意 HttpServlet 父類允許您通過提供可重載的方法( doGet() 和 doPost() ),同時處理 GET 和 POST 請求。

這種方法的主要缺點在於,憑據驗證代碼(它很可能要訪問數據庫)對 ASP.NET 例子來 說是代碼分離文件的一部分,對 J2EE 例子來說是 servlet 的一部分。如果不同的頁面需要 使用這個邏輯,您就必須重復它。重復的代碼更難於維護和更易於出錯,因為您必須跟蹤多 個副本。

模型-視圖-控制器(MVC)方法

下面我們將展示如何對這個例子使用更純的 MVC 方法。在 ASP.NET 端,這體現為將憑據 驗證邏輯轉移到一個單獨的類庫中,然後您可以在代碼分離文件中訪問該類庫。在 J2EE 端 ,憑據驗證代碼將轉移到一個單獨的類中,然後從 servlet 訪問該類。清單 27 中的代碼片 斷顯示了該 servlet 看起來的樣子。

清單 27. J2EE 中的 MVC Java Servlet

import com.ibm.businessobjects.UserValidation
public class LoginServlet extends HttpServlet {
/**
  * Handles all HTTP POST requests
  *
  * @param request Object that encapsulates the request to the servlet
  * @param response Object that encapsulates the response from the servlet
  */
public void doPost(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws ServletException, IOException {
try {
      String userName = request.getParameter("username");
      String password = request.getParameter("password");
      UserValidation user = new UserValidation();
      if (user.validate(userName, password)) {
       response.sendRedirect("mainpage.jsp");
      }
       ...
} catch (Throwable t) {
  ...
}
}
}

清單 28 顯示了在一個單獨的類中的憑據驗證代碼。

清單 28. J2EE 中的 MVC User-validation class

package com.ibm.businessobjects;
public class UserValidation {
  public boolean validate(String username, String password) {
   ...
  }
}

憑據驗證 servlet 所使用的類中進行。現在該 servlet 不必知道關於驗證如何進行的信 息。只要您保持相同的公開接口不變,就可以隨意更改 UserValidation 類,不再需要改變 servlet 代碼。

其他 J2EE 組件

前面的例子展示了如何將業務邏輯分離到一個或一組單獨的類中。這樣允許您以各種不同 的方式實現業務邏輯,而不會影響 servlet 和 JSP 頁面。與使用一個需要訪問數據庫(使 用 JDBC)的業務對象不同,您可以通過各種不同的方式實現這個邏輯。例如:

使用 JCA 來訪問諸如大型機等遺留系統:JCA 是一個 J2EE API,它允許 J2EE 應用程序 使用標准接口來與遺留系統(比如 CICS 或者 IMS)交互。

使用 Web 服務客戶端 API 來訪問 Web 服務:這種方法涉及使用 Apache Axis 或其他某 種 Java Web 服務客戶端 API 來訪問 Web 服務。

使用 JMS 將消息作為驗證請求來發送:這種方法涉及使用標准 API 來訪問 JMS 相容的 消息中間件(比如 WebSphere MQ)。

使用 EJB 技術來實現驗證邏輯:這種方法涉及使用 EJB 組件(而不是簡單的 Java 類) 來實現業務邏輯。EJB 組件能夠使用應用服務器中的服務來管理(數據庫或其他)事務,管 理方法級的安全,以及提供群集和高速緩存。將此類任務委托給應用服務器,您可以把精力 集中在業務邏輯上,而不必擔心系統級的問題。

J2EE 數據訪問

下面我們將介紹 JDBC API,這是 J2EE 下的行業標准接口集,它允許 Java 程序使用統 一的 API 調用集來訪問關系數據庫。JDBC 允許您開發應用程序一次,然後針對 JDBC 相容 的任何數據庫部署它們――DB2、Oracle、SQL Server、Sybase、mySQL、Informix 以及其他 數據庫。

JDBC API 允許任何 Java 組件(包括 Java Servlets 和 JSP 頁面)無縫地訪問關系數 據庫中的數據。 JDBC 由兩部分組成:用於訪問數據庫的 API,以及數據庫廠商為各自的數 據庫提供的可插入 JDBC 驅動程序。JDBC API 最便利的特性之一在於,您不必使用任何廠商 專用的類。您可以在運行時使用一個字符串常量來指定適當的驅動程序,以後就可以使用相 同的編程接口,而不管所使用的是哪種數據庫。這樣允許您切換數據庫,而不必更改代碼( 只要您提供在用於運行時指定驅動程序的字符串常量)。

一個 JDBC 例子

典型的 JDBC 應用程序必須完成以下步驟才能與數據庫交互:

識別驅動程序。(每個應用程序只需執行這個步驟一次。)

識別數據庫並與之建立連接(在需要時提供身份驗證信息)。

執行查詢和/或更新。

處理結果。

斷開與數據庫的連接。

圖 29 說明了上述步驟。

清單 29. 一個 JDBC 例子

// Step 1. SPECIFY THE APPROPRIATE DRIVER TO USE
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");
// Step 2. Identify the database and connect to it
Connection conn = DriverManager.getConnection ("jdbc:db2:SAMPLE","userid","password");
// Step 3. Execute query
Statement stmt = conn.createStatement();
stmt.execute("SELECT * FROM USERID.EMPLOYEE");
// Step 4. Get the results
ResultSet rs = stmt.getResultSet();
while (rs.next()) {
  String firstCol = rs.getString(1);
  int secondCol  = rs.getInt(2);
  ...
}
// Step 5. Disconnect from the database
conn.close();

清單 29 中的 Step 1 動態地加載適當的驅動程序。這個例子使用的是 DB2;其他 JDBC 驅動程序具有不同的完全限定名稱。Step 2 提供特定於驅動程序的 String 來指出要連接到 的數據庫。每種 JDBC 驅動程序都有自己的 String 格式。DB2 使用的格式是 "jdbc:db2:DBNAME" ,因此在此例中要連接到的是一個名為 SAMPLE 的數據庫。例子中還提 供了身份驗證信息,以便數據庫服務器能夠對連接請求進行身份驗證。在 Step 4 中,注意 例子中如何循環迭代結果集,直至 next() 方法返回 false 。您還必須知道每個列的類型, 並對每個列和每種類型調用適當的 getXXXX(n) 方法。還要注意您傳遞給 getXXXX(n) 方法 的整數是列編號。

這種方法相當類似於使用 ADO.NET DataReader,因為程序員要負責打開和關閉數據庫連 接。在 ADO.NET 中,您最初對數據庫使用 ADO.NET 提供程序所提供的類,因此不能像使用 JDBC 那樣容易地在運行時切換數據庫。

連接池

ADO.NET 還提供一組稱為 DataAdapter 的類,它們允許在斷開連接的模式下操作,從而 使您不必顯式地管理數據庫連接。使用 DataAdapter 的典型應用流程如下:

使用提供程序的 DataAdapter 類來識別數據庫(在需要時提供身份驗證信息)和查詢。

執行查詢。

處理結果。

使用 JDBC,您可以從顯式地管理連接的工作中解放出來。可以配置 J2EE 應用服務器來 維護可通過 JDBC 訪問的數據庫的數據庫連接池。然後應用程序就可以從應用服務器管理的 池中請求連接,並在完成使用時將它們返回池中。大多數應用服務器都具有管理連接池的相 當高級的選項,包括“喚醒”連接的能力,即從沒有在給定時間內將連接返回池中的錯誤應 用程序那裡取回連接。

使用連接池的應用程序的典型流程如下:

查找應用服務器資源庫中的 DataSource (用於訪問特定的連接池)。(每個應用服務器 都有一個資源庫,其中包含所有已配置的連接和其他資源,比如消息隊列和 SMTP 提供程序 。您可以使用名為 JNDI 的標准 J2EE API 來訪問資源庫。)(您只需執行這個步驟一次。 )

從池中獲得連接(在需要時提供身份驗證信息)。

執行查詢和/或更新。

處理結果。

將連接返回池中。

清單 30 顯示了使用連接池的一個例子。

清單 30. 一個連接池例子

// Step 1. Look up the DataSource
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("jdbc/MyDS");
// Step 2. Get a connection from the pool
Connection conn = ds.getConnection();
// Step 3. Execute query
Statement stmt = conn.createStatement();
stmt.execute("SELECT * FROM USERID.EMPLOYEE");
// Step 4. Get the results
ResultSet rs = stmt.getResultSet();
while (rs.next()) {
  String firstCol = rs.getString(1);
  int secondCol  = rs.getInt(2);
  ...
}
// Step 5. Return connection to the pool
conn.close();

Step 1 假設應用服務器已經使用名為 "jdbc/MyDS" 的 DataSource 來配置好了。這樣就 封裝了一個特定的 JDBC 驅動程序和數據庫。您還可以向 DataSource 定義添加身份驗證信 息,這樣應用程序就不必提供該信息。可以使用 InitialContext (它是 JNDI API 的一部 分)來查找應用服務器控制的各種資源。Step 3 至 Step 5 與前一個例子相同,不過要注意 Step 5 中對 close() 方法的調用僅把連接返回池中,而不是關閉該數據庫連接。

J2EE 應用程序狀態管理

在編寫 J2EE Web 應用程序時,可以任意選擇一組豐富的類和接口來管理應用程序的狀態 。我們將介紹 J2EE HttpSession 類(它類似於 ASP.NET HttpSessionState 類),以及允 許您管理應用程序狀態的其他類和接口。我們還會討論如何同時在 Java Servlets 和 JSP 頁面中使用這些類和接口。不過,我們將首先介紹 范圍的概念,它是理解 J2EE 中的應用程 序狀態管理的關鍵。

范圍

從程序員的角度看,狀態管理涉及臨時地存儲數據和在需要時檢索它們。在 J2EE 中,您 可以選擇多個“存儲位置”,每個位置具有它自己規則,控制著所存儲的任何數據在多長時 間內可用。持續時間范圍從處理特定頁面時臨時存儲一些數據到在應用程序運行生命期內存 儲數據不等。J2EE 中的“存儲位置”選擇稱為特定存儲請求或檢索的 范圍。該范圍決定了 您將把數據附加到哪些 J2EE 對象,以及那些數據將在多長時間內可用。可用的范圍寶庫o:

會話:這類似於 ASP.NET 中的會話范圍。只要會話還是活動的,您就可以在該用戶會話 范圍內放置任何對象並檢索它。J2EE 應用程序使用 HttpSession 接口(類似於 ASP.NET 中 的 HttpSessionState )。對象通過一個 String 作為標簽來添加到會話中,並使用相同的 標簽來檢索它。

請求:在 J2EE 中, HttpServletRequest 對象允許您向它附加數據,這非常類似 HttpSession 接口。當多個資源處理單個請求時,這是很有用的。例如,某個 Java Servlet 可能是一個 HTML 表單提交的目標,然後它將請求轉發給一個 JSP 頁面以完成它。在這個例 子中,該 sevlet 能夠向 HttpRequest 對象附加數據,並且 JSP 頁面能夠訪問它。注意在 這種場景中,該 servlet 和 JSP 頁面都使用相同的 HttpRequest 對象。向相同請求內的不 同資源轉發的能力類似於 ASP.NET 中的 Server.Transfer 。

應用程序:所有 J2EE Web 應用程序在部署之前都打包到一個具有 .war 擴展名的文件中 。該文件的格式是標准的,因此您可以把同一個應用程序部署到不同的應用服務器。單個 .war 文件中的所有 J2EE 組件都被認為是同一個應用程序的組成部分,並且共享共同的應用 程序上下文。這是通過 ServletContext 接口向開發人員公開的,這個接口(就像 HttpSession 和 HttpRequest 接口一樣)允許您附加和刪除任何 Java 對象。只要應用程序 還在運行,添加到 ServletContext 的項就可用,並且會在單獨會話的創建和銷毀過程中保 留下來。

頁面:頁面上下文在處理單個頁面的過程中可用。例如,JSP 頁面頂部的 Java scriptlet 能夠在 PageContext 中放置對象,然後相同頁面中的其他 scriptlet 就可以訪 問它。

管理應用程序狀態

現在您已經對范圍有了更好的了解,下面我們可以深入地討論管理 J2EE 應用程序中的狀 態的機制。最佳實踐堅持認為,對於任何臨時的狀態存儲,您都應該確定需要存儲該數據多 長時間,然後使用滿足需要的、具有最短生存時間的范圍。例如,假設您需要某個 JSP 頁面 中的數據,該 JSP 頁面是從某個 servlet 轉發的請求的目標。雖然會話狀態和應用程序狀 態也滿足您的需要,但是在這兩種情況下,數據都會在使用完之後懸置在那裡。這樣不必要 地增加了當前應用程序的資源需求。對於這個例子,請求范圍能夠滿足需要,卻不會在您不 需要之後還將數據懸置在那裡。

管理 JSP 頁面中的狀態

在 JSP 腳本環境中,所有范圍對象都是用隱含變量來表示的。您可以在自己的 sciptlet 中使用這些變量,而不需要顯式地聲明它們。 可以使用的隱含變量包括:

session:實現 HttpSession 接口的類的一個實例

application:實現 HttpSession 接口的類的一個實例

request:實現 HttpServletRequest 接口的類的一個實例

pageContext: PageContext 類的一個實例

清單 31 顯示了如何在 JSP scriptlet 中針對不同的范圍添加和刪除對象。

清單 31. JSP 頁面中的狀態管理

<%@ page contentType="text/html; charset=iso-8859-1" language="java" session="true" %>
<%
String foo = "I am a Foo";
// Place object in session scope
session.setAttribute("Foo", foo); 
// Retrieve from session scope
String sessionFoo = (String) session.getAttribute("Foo");
// Place object in application scope
application.setAttribute("Foo", foo); 
// Retrieve from application scope
String applicationFoo = (String) application.getAttribute("Foo");
// Place object in page scope
pageContext.setAttribute("Foo", foo);
// Retrieve from page scope
String pageFoo = (String) application.getAttribute("Foo");
// Place object in request scope
request.setAttribute("Foo", foo);
// Retrieve from request scope
String requestFoo = (String) request.getAttribute("Foo");
%>
....

清單 31 中的例子的第一行是 page 指令,它允許您定義整個頁面的屬性。請注意 session 屬性的使用,它決定了該頁面是否要使得會話可用。如果將它設置為 true 卻還沒 有建立會話,那麼新的會話就會自動為您創建。如果將它設置為 false,那麼會話范圍將在 該 JSP 頁面中不可用。(這個屬性的面默認設置是 true,因此這裡使用它是多余的,只是 出於說明目的。)

清單 32 是使用各種可用范圍來存儲和檢索數據的 servlet 的一個例子。

清單 32. servlet 中的狀態管理

public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
performTask(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
performTask(request, response);
}
/**
  * Handles all HTTP GET and POST requests
  *
  * @param request Object that encapsulates the request to the servlet
  * @param response Object that encapsulates the response from the servlet
  */
public void performTask(
javax.servlet.http.HttpServletRequest req,
javax.servlet.http.HttpServletResponse res)
throws ServletException, IOException {
try {
       // This is how you create a session if is has not been created yet
   // Note that this will return the existing session if you've
   // already created it
       HttpSession session = req.getSession();
  //This is how the application context is retrieved in a servlet
  ServletContext application = getServletContext();
       String foo = "I am a Foo";
       // Place object in session scope
       session.setAttribute("Foo", foo); 
       // Retrieve from session scope
       String sessionFoo = (String) session.getAttribute("Foo");
       // Place object in application scope
       application.setAttribute("Foo", foo); 
       // Retrieve from application scope
       String applicationFoo = (String) application.getAttribute ("Foo");
       // Place object in request scope
       request.setAttribute("Foo", foo);
       // Retrieve from request scope
       String requestFoo = (String) request.getAttribute("Foo");
       ...
} catch (Throwable t) {
  // All errors go to error page
  throw new ServletException(t.getMessage());
}
}
}

JSP 頁面和 sevlet 中的會話之間的區別在於,在 sevlet 中,您必須顯式地創建和檢索 它,而在 JSP 頁面中,這是自動為您完成的。注意 pageContext 僅適用於 JSP 頁面,而不 適用於 servlet;還要注意 servlet API 如何提供了兩個重載方法來同時處理 GET 和 POST HTTP 請求。這個例子提供了一個名為 performTask() 的通用方法來同時處理這兩種類型的 請求,不過您可以基於觸發 servlet 調用的 HTTP 類型而使用不同的處理。

編程方式的導航

在代碼中從一個資源導航到另一個資源的情況在 Web 應用程序中是相當普遍的。導航與 應用程序狀態密切相關,因為當你在一系列資源中導航時,可能需要維護狀態信息。例如, 如果從 sevlet 向 JSP 頁面轉發請求,那麼您可能希望附加某些在該 servlet 中完成的處 理結果,以便這些結果能夠顯示在 JSP 頁面中。

有三個 ASP.NET 方法支持編程方式的導航:

HttpResponse.Redirect() 導致向客戶端浏覽器返回一個特殊的 HTTP 返回代碼(連同要 重從定向的頁面),然後客戶端浏覽器又對重定向的目標發出新的請求。如果需要在這兩個 請求之間共享數據,那就必須將數據存儲在會話狀態中。

Server.Transfer() 導致調用此方法的資源終止,同時終止對作為轉移目標的資源的調用 。對客戶端浏覽器來說,這看起來就像是單個請求。

Server.Execute() 導致調用此方法的資源掛起,同時掛起對目標資源的調用。當目標資 源完成時,掛起的資源又繼續處理。 對客戶端浏覽器來說,這看起來就像是單個請求。

J2EE 支持以下形式的編程式導航:

適用 HttpServletResponse.sendRedirect() 方法 :這會導致向客戶端浏覽器返回一個 特殊的 HTTP 返回代碼(連同要重從定向的頁面),然後客戶端浏覽器又對重定向的目標發 出新的請求。如果需要在這兩個請求之間共享數據,那就必須將數據存儲在會話或應用程序 范圍中。

在 servlet 或 JSP 頁面的特殊標簽中使用 RequestDispatcher.forward() 方法 :這會 導致調用此方法的資源終止,同時終止對作為轉發目標的資源的調用。對客戶端浏覽器來說 ,這看起來就像是單個請求。

在 JSP 頁面的特殊標簽中使用 RequestDispatcher.include() 方法 :這會導致調用此 方法的資源掛起,同時掛起對目標資源的調用。當目標資源完成時,掛起的資源又繼續處理 。 對客戶端浏覽器來說,這看起來就像是單個請求。

清單 33 包括了這些導航方法在 JSP 頁面中的一些例子。

清單 33. JSP 頁面中的編程式導航

<!-- This scriptlet shows how you can redirect to another resource - ->
<%
response.sendRedirect("anotherPage.jsp");
%>
<!-- This special JSP tag allows you to forward the request to another resource -->
<jsp:forward page="anotherPage.jsp"/>
<!-- This special JSP tag allows you to include another resource as
part of the processing of this page -->
<jsp:include page="anotherPage.jsp" flush="true"/>

清單 34 顯示了這些導航方法在 servlet 中的一些例子。

清單 34. servlet 中的編程式導航

public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  ...
   // Redirecting to another resource
   response.sendRedirect("anotherResource.jsp");
  ...
}
// This code snippet shows the use of a RequestDispatcher to forward to another resource
public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  ...
  // Get a RequestDispatcher instance
  RequestDispatcher rd = getServletContext().getRequestDispatcher ("anotherResource.jsp");
  // Forward the request
  rd.forward(request, response);
  ...
}
// This code snippet shows the use of a RequestDispatcher to include another resource
public void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
  ...
  // Get a RequestDispatcher instance
  RequestDispatcher rd = getServletContext().getRequestDispatcher ("anotherResource.jsp");
  // Forward the request
  rd.include(request, response);
  // Continue processing the request
  ...
}

Cookie

在 ASP.NET 中,cookie 的處理使用了 HttpRequest 和 HttpResponse 對象的 Cookies 屬性。這個屬性表示 HttpCookie 對象的一個集合,集合中的每個成員分別表示一個單獨的 cookie。

在 J2EE 中,cookie 使用 Cookie 類來表示,並且可通過 HttpServletRequest 和 HttpServletResponse 接口來訪問它們。現有的 cookie 使用 HttpServletRequest 接口來 訪問,新的 cookie 使用 HttpServletResponse 接口來創建。

清單 35 演示了如何在 J2EE 應用程序中使用 cookie。你可以在 Java Servlet 或 JSP 頁面的 scriptlet 中使用這些代碼片斷。

清單 35. 在 J2EE Web 應用程序使用 cookie

// Create a cookie using the HttpServletResponse interface
// The first parameter of the constructor is the name of the cookie
// and the second parameter is the cookie's value
Cookie myCookie = new Cookie("myCookie", "someValue");
// Set the age of the cookie - by default cookies will be stored in memory
// by the browser and will be destroyed when the user closes the browser
// The setMaxAge() methods takes an integer as its parameter which represents
// the age in seconds. In this example we're specifying an age of 24 hours for the cookie
myCookie.setMaxAge(86400);
// Add cookie to the response
response.addCookie(myCookie);
// Here's an example of retrieving cookies from the HttpServletRequest interface
// Note that the cookies are returned as an array of Cookie objects
Cookies[] myCookies = request.getCookies();
// The getCookies method will return null if there are no cookies
if (myCookies != null) {
  for (int i = 0; i < myCookies.length; i++) {
    Cookie eachCookie = myCookies[i];
    // Do something w/each cookie
    ....
  }
}

結束語

感謝您使用這個關於 J2EE 開發的入門教程。我們已盡力提供足夠的信息使您走上 J2EE 之路,並且使您的開放標准編程之旅盡可能的順利。我們鼓勵您利用本文的 參考資料小節以 繼續接受 J2EE 教育。祝旅途愉快。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved