流控制 面向对象设计方法的最大好处是可重用性。特别是,J2EE 系统将它们借用到模块化风格的开发中,其中组件可以在其他应用程序中重新安排、重新打包和重新使用。即使您对设计可重用的 Web 模块不感兴趣,也很可能会发现您的 J2EE 应用程序由几个部分组成。任何时候使用多个 servlet 或者 JSP 页面(也就是组件)完成一个请求的时候,都需要使用某种类型的流控制技术。Servlet 架构提供两种这样的技术:forward(转发) 和 include(包括)。
在 J2EE Web 开发中, forward会把处理用户请求的控制权转交给到其他 Web 组件。forward 在有些时候会比较有用,比如说需要用一个组件设置一些 JavaBean、打开或关闭资源、认证用户,或者在将控制权传递给下一个组件之前需要执行一些准备工作。在转发之前可以执行很多类型的任务,但是要转发的组件不能设置响应头部信息,也不能有内容发送到输出缓冲区。所有与向客户发送内容直接相关的任务必须由被转发的组件完成。
J2EE 中第二种流控制技术是 include。在使用 forward 时,要传递控制权。与此不同的是,执行 include 的组件维持对请求的控制权,而只是简单地请求将另一个组件的输出包括在该页面的某个特定的地方。对于常见的设计元素,例如页首、页脚和导航栏等,这是一个非常好的方法。
forward 和 include 都是通过一个专门的对象 java.servlet.RequestDispatcher 来完成的。简单地调用一个 ServletContext 对象的 getRequestDispatcher() 方法就可以获得一个 RequestDispatcher 对象。得到对 ServletContext 对象的引用有几种方法,我们可以:
- 使用隐式声明的 application 变量,因为它的类型本身已经是 ServletContext。
- 调用方法 getServletContext() ,该方法返回一个对隐式声明的 application 变量的引用。
- 调用隐式声明的 config 变量的 g etServletContext() 方法 。
- 调用隐式声明的 pageContext 变量的 getServletContext() 方法 。
- 调用隐式声明的 request 变量的 getServletContext() 方法 。
- 调用隐式声明的 session 变量的 getServletContext() 方法 。
清单1给出了使用隐式变量 application 的 forward 流控制机制的代码示例。
清单1. forward 流控制示例1
2
3
4
5
6
7
8
| javax.servlet.RequestDispatcher rd;
/* Obtain a reference to a RequestDispatcher object via the implicit
application variable*/
rd = application.getRequestDispatcher( "/NextPage.jsp" );
/* Perform the forward specified by the RequestDispatcher
and pass along a reference to the current request and
response objects */
rd.forward( request, response );
|
清单2给出了同样使用变量 application 的 include 流控制的代码示例。
清单2. include 流控制示例1
2
3
4
5
6
7
8
| javax.servlet.RequestDispatcher rd;
/* Obtain a reference to a RequestDispatcher object via the implicit
application variable*/
rd = application.getRequestDispatcher( "/Header.jsp" );
/* Perform the include specified by the RequestDispatcher
and pass along a reference to the current request and
response objects */
rd.include( request, response );
|
forward 和 include 是添加到 J2EE Web 开发工具包中的两个非常棒的技术。还有其他一些方法可以在 JSP 页面中完成 include,而且还有很多解决 J2EE 设计模式方面的文献中讲到了如何结合使用这两种技术。参阅 参考资料以了解更多信息。
日志记录和异常 如果您需要把与 Web 应用程序相关的信息存储到一个日志中,依然有内建的方法可用。 ServletContext 接口声明了两个方法,用于把数据传给一个日志。其中一个方法接受简单的文本消息: log( java.lang.String ) ,另一个方法接受一个异常信息和一个文本消息: log(java.lang.Throwable, java.lang.String ) 。
在有了 ServletContext 接口提供的两个可用的日志记录方法之后,剩下的关键是获取一个对 ServletContext 类型的对象的引用。像我们前面讨论过的流控制对象一样,有多种方法可以获取对 ServletContext 类型的对象的引用。在获得了对象引用之后,简单地调用 log() 方法并向方法中传递必需的数据即可。一旦调用了这个方法,您当然就会希望能够查看应用程序日志以查看消息。 ServletContext 是一个简单的接口,并且也没有规定怎样实现它声明的方法。因而 log 方法的具体实现是由供应商处理的。他们可以把日志信息存储到一个文本文件、二进制文件、数据库中,或者是供应商认为合适的其他格式中。您需要从服务器的文档中得知存储日志的位置。
虽然向一个日志文件发送消息相当有用,但是很多时候您可能还想在发生不可恢复的异常时显示一个用户友好的错误消息。要实现这一功能,您可以声明,您的 JSP 页面使用一个单独的页面来处理错误消息。这是在 JSP 页面的任何地方通过包含下面的 page 指令实现的:
1
| <%@ page errorPage="ErrorMessage.jsp"%>
|
如果在处理 JSP 页面时有一个异常抛出的话,exception 对象就会立即通过隐式声明的 exception 变量的方式抛给指定的错误页面。
为了使一个 JSP 页面能够作为一个错误页面,它必须包含一个指令来声明这个页面是指定用来处理错误的特殊页面,指令如下:
1
| <%@ page isErrorPage="true"%>
|
为了使用 ErrorMessage.jsp 页面能够作为一个错误页面,这个指令必须出现在页面的某个地方。错误页面可以显示一个友好的信息给用户,然后可以将相关的异常信息写入日志以供管理员日后查看。
输入和输出控制 因为 JSP 页面仅仅是 HTTP servlet 的一个简单抽象,所以您可以访问 HttpServletRequest 和 HttpServletResponse 对象。如果需要特定于请求的信息,比如客户机浏览器的类型、HTTP post 的内容类型、客户机性能、Cookie 数据或者请求参数,简单地用隐式声明的 request 变量直接调用适当的方法即可。类似地,如果您需要设置响应头部信息,比如说浏览器类型、内容类型、内容长度等等,简单地用隐式变量 response 调用适当的方法即可。
如果需要直接访问 JSP 页面的输出流,您可能会试图通过隐式 response 变量调用 getWriter() 或 getOutputStream() 。然而由于 JSP 页面的特殊性,您不能这样做。如果需要直接访问输出流,必须通过一个 avax.servlet.jsp.JSPWriter 类型的特殊缓冲 PrintWriter 对象来访问。怎样定位这样一个对象的引用呢?JSP 容器将会为您隐式地声明一个,并通过 out 变量提供给您。在 JSP scriptlet 中可以通过简单地调用 out.print() 或 out.println() 使用它。
一般来说不需要像这样直接使用 JSPWriter 对象,而只需简单地把内容作为普通文本或者通过 JSP 表达式写入,然后允许容器将这些信息翻译成 JSPWriter 调用。然而,在两种情况下您需要直接使用 out 变量。一种情况是要为 JSP 自定义标记定义处理程序,这部分内容我们将在下个月重点讲到。另外一种情况是您想要对 JSP 创建的输出拥有更多的控制。如果您有一段夹杂着 JSP scriptlets 和表达式的 HTML,您可能会发现创建大的 scriptlet 然后在需要输出内容到客户机的时候使用 out.println() 语句这样做会更简洁、更容易。
初始化参数 如果您有一些静态数据想提供给 JSP 页面使用,并且那些数据不会频繁地改动,初始化参数可能会是一个比较好的选择。初始化参数有时候又叫环境变量或者“init”参数,这些参数通过位于一个 per-servlet/JSP 内的 Web 应用程序的 web.xml文件指定,并且它们在servlet 的生命周期中只读取一次,即在初始化时读取。
清单3是一个初始化参数声明的例子。
清单3. 初始化参数声明1
2
3
4
5
6
7
8
9
10
| <webapp>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.gabhart.MyTestServlet</servlet-class>
<init-param>
<param-name>contactEmail</param-name>
<param-value>kyle@gabhart.com</param-value>
</init-param>
</servlet>
</webapp>
|
使用隐式变量 config 可以访问这些参数的值,隐式变量 config 是对 JSP 页面的 ServletConfig 对象的引用。通过 ServletConfig 接口提供了两个处理 init 参数的方法。可以根据名字对一个特定的参数完成一个查找( getInitParameter( java.lang.String) ), 或者也可以检索到为 JSP 页面定义的所有参数名字的一个 enumeration( getInitParameterNames() )。在拥有了enumeration 之后,可以通过循环查找每一个值。所有 init参数都是 String 对象。如果需要其他的数据类型,比如说整数、浮点数或者布尔值,必须使用相应的包装器类来解析字符串。
结束语JSP 技术提供了 Servlet 架构之上的一个非常有用的抽象,它让 Web 设计者可以着重关注内容表示,而只要求知道较少的编程细节。在本文中,您已经看到了我们是如何使用隐式对象来快速、容易地开发 Web 应用程序的。
下个月,我们将开始讲述 JSP 自定义标记和 JSP 标准标记库(JSTL)。学习自定义标记如何促进表示和业务逻辑之间的分离,同时还让您可以将动态数据合并到表示层。到那时,一起快乐地探索吧! |