J2EE 应用程序状态管理
在编写 J2EE Web 应用程序时,要管理应用程序的状态,有一组丰富的类和接口可供选择。我们将介绍这些类和接口,并讨论如何在 Java Servlets 和 JSP 页面中使用这些类和接口。不过,我们将首先介绍 范围的概念,它是理解 J2EE 中的应用程序状态管理的关键。
范围
从程序员的角度看,状态管理涉及临时存储数据和在需要时检索它们。在 J2EE 中,您可以选择多个“存储位置”,每个位置具有它自己规则,这些规则控制任何存储的数据得有效时间。持续时间从处理某个特定页面所需的时间(临时存储一些数据),到应用程序运行生命期(长期存储数据)不等。J2EE 中的“存储位置”选择称为特定存储请求或检索的 范围。该范围决定了您将把数据附加到哪些 J2EE 对象,以及那些数据将在多长时间内可用。可用的范围是:
会话:这类似于 ASP 中的会话范围。只要会话还是活动的,您就可以在该用户会话范围内放置任何 Java 对象并检索它。J2EE 应用程序使用 HttpSession 接口(类似于 ASP 中的 SESSION )。可以使用 String 作为标签将对象添加到会话中,并使用相同的标签来检索它。
请求:在 J2EE 中, HttpServletRequest 对象允许您向它附加数据,这非常类似 HttpSession interface 接口。当多个资源处理单个请求时,这是很有用的。例如,某个 Java Servlet 可能是一个 HTML 表单提交的目标,然后它将请求转发给一个 JSP 页面以完成它。在这个例子中,该 sevlet 能够向 HttpRequest 对象附加数据,并且 JSP 页面能够访问它。注意在这种场景中,该 servlet 和 JSP 页面都使用相同的 HttpRequest 对象。
应用程序:所有 J2EE Web 应用程序在部署之前都打包到一个具有 .war 扩展名的文件中。该文件的格式是标准的,因此您可以把同一个应用程序部署到不同的应用服务器。单个 .war 文件中的所有 J2EE 组件都被认为是同一个应用程序的组成部分,并且共享共同的应用程序上下文。这是通过 ServletContext 接口向开发人员公开的,这个接口(就像 HttpSession 和 HttpRequest 口一样)允许您附加和删除任何 Java 对象。只要应用程序还在运行,添加到 ServletContext 的项就可用,并且会在单独会话的创建和销毁过程中保留下来。这类似于 ASP 中的 APPLICATION 对象。
页面:页面上下文在处理单个页面的过程中可用。如,JSP 页面顶部的 Java scriptlet 能够在 PageContext 中放置对象,然后相同页面中的其他 scriptlet 就可以访问它。
管理应用程序状态
现在您已经对范围有了更好的了解,下面我们可以深入地讨论管理 J2EE Web 应用程序中的状态的机制。非常好的实践是,对于任何临时的状态存储,您都应该确定需要存储该数据多长时间,然后使用满足需要的、具有最短生存时间的范围。例如,假设您需要某个 JSP 页面中的数据,该 JSP 页面是从某个 servlet 转发的请求的目标。虽然会话状态和应用程序状态也满足您的需要,但是在这两种情况下,数据都会在使用完之后悬置在那里。这样不必要地增加了当前应用程序的资源需求。对于这个例子,请求范围能够满足需要,却不会在您不需要之后还将数据悬置在那里。
管理 JSP 页面中的状态
在 JSP 脚本环境中,所有范围对象都是用隐含变量来表示的。您可以在自己的 sciptlet 中使用这些变量,而不需要显式地声明它们。可以使用的隐含变量有:
session: 实现 HttpSession 接口的类的一个实例。
application:实现 HttpSession 接口的类的一个实例。
request:实现 HttpServletRequest 接口的类的一个实例。
pageContext: PageContext 类的一个实例。
清单 31 展示了如何在 JSP scriptlet 中针对不同的范围添加和删除对象。
清单 31. JSP 页面中的状态管理
2 <%
3 String foo = "I am a Foo";
4 // Place object in session scope
5 session.setAttribute("Foo", foo);
6 // Retrieve from session scope
7 String sessionFoo = (String) session.getAttribute("Foo");
8 // Place object in application scope
9 application.setAttribute("Foo", foo);
10 // Retrieve from application scope
11 String applicationFoo = (String) application.getAttribute("Foo");
12 // Place object in page scope
13 pageContext.setAttribute("Foo", foo);
14 // Retrieve from page scope
15 String pageFoo = (String) application.getAttribute("Foo");
16 // Place object in request scope
17 request.setAttribute("Foo", foo);
18 // Retrieve from request scope
19 String requestFoo = (String) request.getAttribute("Foo");
20 %>
21 ....
22
清单 31 中的例子的第一行是 page 指令,它允许您定义整个页面的属性。请注意 session 属性的使用,它决定了该页面是否要使得会话可用。如果将它设置为 true 却还没有建立会话,那么新的会话就会为您创建。如果将它设置为 false,那么会话范围将在该 JSP 页面中不可用。(这个属性的面默认设置是 true,因此这里使用它是多余的,只是出于说明目的。)
清单 32 是使用各种可用范围来存储和检索数据的 servlet 的一个例子。
清单 32. servlet 中的状态管理
2 public void doGet(
3 HttpServletRequest request, HttpServletResponse response)
4 throws ServletException, IOException {
5 performTask(request, response);
6 }
7 public void doPost(HttpServletRequest request, HttpServletResponse response)
8 throws ServletException, IOException {
9 performTask(request, response);
10 }
11 /**
12 * Handles all HTTP GET and POST requests
13 *
14 * @param request Object that encapsulates the request to the servlet
15 * @param response Object that encapsulates the response from the servlet
16 */
17 public void performTask(
18 javax.servlet.http.HttpServletRequest req,
19 javax.servlet.http.HttpServletResponse res)
20 throws ServletException, IOException {
21 try {
22 // This is how you create a session if is has not been created yet
23 // Note that this will return the existing session if you've
24 // already created it
25 HttpSession session = req.getSession();
26
27 //This is how the application context is retrieved in a servlet
28 ServletContext application = getServletContext();
29
30 String foo = "I am a Foo";
31 session.setAttribute("Foo", foo);
32
33 // Place object in session scope
34 session.setAttribute("Foo", foo);
35 // Retrieve from session scope
36 String sessionFoo = (String) session.getAttribute("Foo");
37 // Place object in application scope
38 application.setAttribute("Foo", foo);
39 // Retrieve from application scope
40 String applicationFoo = (String) application.getAttribute("Foo");
41 // Place object in request scope
42 request.setAttribute("Foo", foo);
43 // Retrieve from request scope
44 String requestFoo = (String) request.getAttribute("Foo");
45
46 ...
47
48 } catch (Throwable t) {
49 // All errors go to error page
50 throw new ServletException(t.getMessage());
51 }
52 }
53
JSP 页面和 sevlet 中的会话之间的区别在于,在 sevlet 中,您必须显式地创建和检索它,而在 JSP 页面中,这是自动为您完成的。注意 pageContext 仅适用于 JSP 页面,而不适用于 servlet;还要注意 servlet API 如何提供了两个要覆盖的方法来处理 GET 和 POST HTTP 请求。这个例子提供了一个名为 performTask() 的通用方法来同时处理这两种类型的请求,不过您可以基于触发 servlet 调用的 HTTP 类型而使用不同的处理。
编程式导航
在代码中从一个资源导航到另一个资源的情况在 Web 应用程序中是相当普遍的。导航与应用程序状态密切相关,因为当您在一系列资源中导航时,可能需要维护状态信息。与 ASP 比较而言,J2EE 提供更多的选项进行编程式导航。
ASP 使用 RESPONSE.REDIRECT() 函数支持编程式导航。J2EE 支持以下形式的编程式导航:
使用 HttpServletResponse.sendRedirect() 方法 :这会导致向客户端浏览器返回一个特殊的 HTTP 返回代码(连同要重定向的页面),然后客户端浏览器又对重定向的目标发出新的请求。如果需要在这两个请求之间共享数据,那就必须将数据存储在会话或应用程序范围中。这类似于 ASP 中的 RESPONSE.REDIRECT() 函数。
使用 servlet 中的 RequestDispatcher.forward() 方法或 JSP 页面中的特殊标签: 这会导致调用此方法的资源终止,同时终止对作为转发目标的资源的调用。对客户端浏览器来说,这看起来就像是单个请求。例如,如果将请求从 servlet 转发到 JSP 页面,则您可能要附加该 servlet 中的某些处理结果,以便这些结果可以在 JSP 页面中显示出来。
使用 RequestDispatcher.include() 方法或 JSP 页面中的特殊标签 :这会导致调用此方法的资源挂起,同时挂起对目标资源的调用。当目标资源完成时,挂起的资源又继续处理。对客户端浏览器来说,这看起来就像是单个请求。
清单 33 包括了 JSP 页面中这些导航方法的一些例子。
清单 33. JSP 页面中的编程式导航
2 <%
3 response.sendRedirect("anotherPage.jsp");
4 %>
5 <!-- This special JSP tag allows you to forward the request to another resource -->
6 <jsp:forward page="anotherPage.jsp"/>
7 <!-- This special JSP tag allows you to include another resource
8 as part of the processing of this page -->
9 <jsp:include page="anotherPage.jsp" flush="true"/>
10
清单 34 展示了 servlet 中这些导航方法的一些例子。
清单 34. servlet 中的编程式导航
2 throws ServletException, IOException {
3 ...
4 // Redirecting to another resource
5 response.sendRedirect("anotherResource.jsp");
6 ...
7 }
8 // This code snippet shows the use of a RequestDispatcher to forward to another resource
9 public void doGet(HttpServletRequest request, HttpServletResponse response)
10 throws ServletException, IOException {
11 ...
12 // Get a RequestDispatcher instance
13 RequestDispatcher rd = getServletContext().getRequestDispatcher("anotherResource.jsp");
14 // Forward the request
15 rd.forward(request, response);
16 ...
17 }
18 // This code snippet shows the use of a RequestDispatcher to include another resource
19 public void doGet(HttpServletRequest request, HttpServletResponse response)
20 throws ServletException, IOException {
21 ...
22 // Get a RequestDispatcher instance
23 RequestDispatcher rd = getServletContext().getRequestDispatcher("anotherResource.jsp");
24 // Forward the request
25 rd.include(request, response);
26 // Continue processing the request
27 ...
28 }
29
Cookie
在 ASP 中, cookie 的处理使用了 REQUEST 和 RESPONSE 对象的 Cookies 属性。 RESPONSE 对象用于创建 cookie,而 REQUEST 对象用于检索 cookie 值。为方面起见,在代码中使用了多值 cookie,尽管多值 cookie 被编码到 HTTP 响应表头时,但是可以转换为单值 cookie。
在 J2EE 中,cookie 由 Cookie 类表示,并通过 HttpServletRequest 和 HttpServletResponse 接口来访问。您可以使用 HttpServletRequest 接口访问现有的 cookie,使用 HttpServletResponse 接口来创建 cookie。
清单 35 演示了如何在 J2EE 应用程序中使用 cookie。您可以在 Java Servlet 或 JSP 页面的 scriptlet 中使用这些代码片断。
清单 35. 在 J2EE Web 应用程序使用 cookie
2 // The first parameter of the constructor is the name of the cookie
3 // and the second parameter is the cookie's value
4 Cookie myCookie = new Cookie("myCookie", "someValue");
5 // Set the age of the cookie - by default cookies will be stored in memory
6 // by the browser and will be destroyed when the user closes the browser
7 // The setMaxAge() methods takes an integer as its parameter which represents
8 // the age in seconds. In this example we're specifying an age of 24 hours for
9 // the cookie
10 myCookie.setMaxAge(86400);
11 // Add cookie to the response
12 response.addCookie(myCookie);
13 // Here's an example of retrieving cookies from the HttpServletRequest interface
14 // Note that the cookies are returned as an array of Cookie objects
15 Cookies[] myCookies = request.getCookies();
16 // The getCookies method will return null if there are no cookies
17 if (myCookies != null) {
18 for (int i = 0; i < myCookies.length; i++) {
19 Cookie eachCookie = myCookies[i];
20 // Do something w/each cookie
21 ....
22 }
23 }
24
结束语
感谢您使用这个关于 J2EE 开发的入门教程。我们已尽力提供足够的信息使您走上 J2EE 之路,并且使您的开放标准编程之旅尽可能的顺利。