走上开放之路:.NET开发人员的J2EE基础(下)

相关文章:走上开放之路: .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 的一部分 ——例如: 。

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 ut = response.getWriter();  out.println("");  out.println("Hello World");  out.println("");} 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 页面来返回内容

JSP page exampleThe date is:Some more HTMLThe value of e is:Yet even more HTMLThe value of PI is:

注意其中使用了 JSP 隐含变量 out 来将内容写回客户端,还要注意 HTML 和 Java 代码 的交织。

一个例子场景

为了说明各种体系结构选项,我们将使用一个简单的用户登录场景,它包含:

一个具有 HTML 表单的登录页面,它从用户那里收集用户名和密码

验证用户凭据(可能使用数据库)并重定向站点主页的应用逻辑

这个简单的例子允许我们描述各种各样的应用体系结构。

“意大利面条式”代码

在 ASP.NET 方,“意大利面条式”代码方法使用单个 .aspx 文件来同时包含应用逻辑和 HTML 表单。不存在代码分离(code-behind)。(不推荐对现实中的例子采用这种方法,因 为这样所有表示逻辑和应用逻辑都将是单个文件,从而阻止了您复用用于验证用户凭据的代 码。)该代码的轮廓看起来类似清单 23 中所示的代码。

清单 23. ASP.NET 中的“意大利面条式”代码

Login example...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) {    ...}...        

正如清单 24 所示,您可以在 J2EE 中采用相同的方法,使用单个 JSP 页面来同时包含 表单和应用逻辑。

清单 24. J2EE 中的“意大利面条式”代码

Login example...         </form. 

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

Login example...

清单 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.UserValidationpublic 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 USEClass.forName("COM.ibm.db2.jdbc.app.DB2Driver");// Step 2. Identify the database and connect to itConnection conn = DriverManager.getConnection ("jdbc:db2:SAMPLE","userid","password");// Step 3. Execute queryStatement stmt = conn.createStatement();stmt.execute("SELECT * FROM USERID.EMPLOYEE");// Step 4. Get the resultsResultSet rs = stmt.getResultSet();while (rs.next()) {  String firstCol = rs.getString(1);  int secondCol  = rs.getInt(2);  ...}// Step 5. Disconnect from the databaseconn.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 DataSourceInitialContext ic = new InitialContext();DataSource ds = (DataSource) ic.lookup("jdbc/MyDS");// Step 2. Get a connection from the poolConnection conn = ds.getConnection();// Step 3. Execute queryStatement stmt = conn.createStatement();stmt.execute("SELECT * FROM USERID.EMPLOYEE");// Step 4. Get the resultsResultSet rs = stmt.getResultSet();while (rs.next()) {  String firstCol = rs.getString(1);  int secondCol  = rs.getInt(2);  ...}// Step 5. Return connection to the poolconn.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 页面中的状态管理

....

清单 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 页面中的编程式导航

清单 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 resourcepublic 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 resourcepublic 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 valueCookie 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 cookiemyCookie.setMaxAge(86400);// Add cookie to the responseresponse.addCookie(myCookie);// Here's an example of retrieving cookies from the HttpServletRequest interface// Note that the cookies are returned as an array of Cookie objectsCookies[] myCookies = request.getCookies();// The getCookies method will return null if there are no cookiesif (myCookies != null) {  for (int i = 0; i < myCookies.length; i++) {    Cookie eachCookie = myCookies[i];    // Do something w/each cookie    ....  }}

结束语

感谢您使用这个关于 J2EE 开发的入门教程。我们已尽力提供足够的信息使您走上 J2EE 之路,并且使您的开放标准编程之旅尽可能的顺利。我们鼓励您利用本文的 参考资料小节以 继续接受 J2EE 教育。祝旅途愉快。

用开怀的笑容去迎接每一个黎明,

走上开放之路:.NET开发人员的J2EE基础(下)

相关文章:

你感兴趣的文章:

标签云: