|
JavaServer
PagesTM 基础 - 短期教程 课程概要
介绍虽然有多种技术可用于生成为动态内容提供服务的 web 应用程序,但真正受 到开发界青睐的是 JavaServer PagesTM (JSPTM)。这并非没有充分的理由。JSP 不仅 拥有跨平台和跨 Web 服务器支持,并且将服务器端 Java 技术的功能与静态 HTML 页的“所见即所得”功能有效地结合了起来。 典型的JSP 页由以下几部分组成:
因此,您可以用常规的 HTML/XML 工具创建和维护 JSP 页。 有必要指出:JSP 规范是在 Servlet API 上定义的标准扩展。因此,您可以 利用所有与 servlet 相关的经验。 JSP 和 servlet 技术之间区别很大。servlet 是一种编程技术,对开发人员 的专门知识技能要求很高;而 JSP 与 servlet 不同,它适合更广泛的使用者。 不仅开发人员可以使用它,页面设计人员也可以使用它,从而使其在开发周期中 扮演更为直接的角色。 JSP 的另一个优点是演示与使用 JSP 技术的内容的分离,这归功于 JSP 对可 重用组件技术(例如 JavaBeansTM 组件体系 结构和企业级 JavaBeansTM 技术)的依赖。 本教程向您深入介绍这种多能的技术,并使用 Apache 集团的 Tomcat JSP 1.1 Reference Implementation 来运行示例程序。 JSP 优点静态内容和动态内容的分离: 对于 servlet,动态内容的生成逻辑是 servlet 本身的固有部分,与负责用户界面的静态演示模板紧密关联。因此,即 使对 UI 所作的微小更改,通常也会导致重新编译 servlet。演示与内容的这种 紧密结合使得应用程序脆弱而不灵活。但是,对于 JSP,通过将生成动态内容的 逻辑封装到外部 JavaBeans 组件中,可使该逻辑与静态演示模板保持着分离。从 而使用特殊标记和脚本小程序的 JSP 页创建并使用这些组件。当页面设计人员对 演示模板作任何更改时,JSP 引擎便会自动重新编译 JSP 页,并将其重新加载 到 web 服务器中。 一次编写,随处运行: JSP 技术将“一次编写,随处运行”的范例推 广到了交互网页中。不需要作任何更改,就可以很轻松地跨平台和跨 Web 服务器 移动 JSP 页。 可以用多种格式为动态内容提供服务: 没有任何机制使 JSP 页中的静 态模板采用特定的格式。因此,JSP 可以为不同的客户端(包括使用 HTML/DHTML 的常规浏览器、使用 WML 的移动电话和 PDA 等手持式无线设备和使用 XML 的其 他 B2B 应用程序)提供服务。 推荐的用于 n 层体系结构的 Web 访问层: Sun 的 J2EETM,蓝图为使用企业级 Java API 开发大规模的应用程序提供了一些准则,它明确推荐使用 JSP(而不要使用 servlet)为动态内容提供服务。 完全利用 Servlet API: 如果您是 servlet 开发人员,若要转到 JSP,所掌握的知识技能几乎没有什么必须摈弃的。事实上,servlet 开发人员有 明显的优势,因为 JSP 只是对 servlet 的高级提炼。使用 JSP,您几乎可以做 任何使用 servlet 可以做的事情,并且更加容易! JSP 与 ASP 比较尽管 JSP 提供的功能看起来与 Microsoft 的 Active Server Page (ASP) 的 功能相似,但这两种技术有根本的区别,如下表所示:
JSP 还是 servlet?servlet 和 JSP 页确实有许多相同之处,并且都可以用来为动态 Web 内容提 供服务。对于何时选择其中的一种技术而不选择另一种技术,这自然会带来一些 混淆。所幸的是,Sun 的 J2EE 蓝图对此提供了一些准则。 根据该蓝图,严格地将 servlet 用作 Web 服务器扩展技术。这可包括提供认 证、数据库验证等服务的专用控件器组件的实现。注意,有趣的是,通常所说的 “JSP 引擎”本身是一个在 servlet 引擎控制下运行的专用 servlet。由于 JSP 仅处理文本数据,因而您在与 Java applet和应用程序进行通信时必须继续 使用 servlet。 使用 JSP 开发典型的依赖动态内容的 Web 应用程序。还应当使用 JSP 来代 替服务器端包含文件之类的专有 Web 服务器扩展,因为 JSP 具有优异的处理重 复内容的功能。 练习JSP 体系结构JSP 的用途是提供声明的、以演示为中心的开发 servlet 的方法。正如前面 所指出的那样,JSP 规范本身被定义为 Servlet API 上的标准扩展。因此,在实 质上,servlet 和 JSP 页有许多相同之处,这并不会令人感到十分奇怪。 通常,JSP 页具有翻译阶段和请求处理阶段。除非 JSP 页更改(在这种情况 下翻译阶段会重复),否则翻译阶段仅执行一次。假定页面中没有语法错误,翻 译结果是一个 JSP 页实现类文件,该文件实现 Servlet 接口,如下所示。 当 JSP 引擎首次收到传入的对 JSP 页的请求时,JSP 引擎本身通常会执行翻 译阶段。注意,JSP 1.1 规范还允许将 JSP 页预编译到类文件中。对于移除当以 源代码形式传递的 JSP 页从客户端接收到第一个请求时发生的启动日志,预编译 尤其有用。翻译阶段的许多细节(如源文件和类文件的存储位置)都依赖实现。 Tomcat 为此 JSP 页示例(展示在上图中)生成的类文件的源代码如下: package jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Vector;
import org.apache.jasper.runtime.*;
import java.beans.*;
import org.apache.jasper.JasperException;
import java.text.*;
import java.util.*;
public class _0005cjsp_0005cjsptest_0002ejspjsptest_jsp_0
extends HttpJspBase {
static {
}
public _0005cjsp_0005cjsptest_0002ejspjsptest_jsp_0( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws JasperException {
}
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html");
pageContext = _jspxFactory.getPageContext(this,
request,response, "", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// begin
out.write("\r\n<html>\r\n<body>\r\n");
// end
// begin [file="E:\\jsp\\jsptest.jsp";from=(3,2);to=(5,0)]
Date d = new Date();
String today = DateFormat.getDateInstance().format(d);
// end
// begin
out.write("\r\nToday is: \r\n<em> ");
// end
// begin [file="E:\\jsp\\jsptest.jsp";from=(7,8);to=(7,13)]
out.print(today);</b>
// end
// begin
out.write(" </em>\r\n</body>\r\n</html>\r\n");
// end
} catch (Exception ex) {
if (out.getBufferSize() != 0)
out.clear();
pageContext.handlePageException(ex);
} finally {
out.flush();
_jspxFactory.releasePageContext(pageContext);
}
}
}
JSP 页实现类文件扩展 一旦在 servlet 容件中加载此类文件后, JSP 访问模型早期的 JSP 规范提出了两种应用 JSP 技术的理论方法,通常称为 Model 1 和 Model 2 体系结构。这两种方法主要在请求处理主体的执行位置上有区别,它 们为使用 JSP 技术生成应用程序提供了一个有用的范例。 试考虑 Model 1 体系结构,如下所示: 在 Model 1 体系结构中,从 Web 浏览器传入的请求直接发送到 JSP 页,该 页负责处理请求及回复客户端。演示与内容仍然分离,因为所有数据访问都是通 过使用 bean 执行的。 尽管 Model 1 体系结构适合简单应用程序,但可能不适合复杂的实现。不加 选择地使用这种体系结构通常会使大量脚本小程序或 Java 代码嵌入到 JSP 页中, 尤其在有大量请求处理需要执行时更是如此。虽然这对 Java 开发人员似乎不是 一个大问题,但如果您的 JSP 页由设计人员创建并维护(大型项目通常如此), 这肯定是一个问题。这种体系结构的另一个缺陷是,每个 JSP 页必须单独负责管 理应用程序状态和验证认证与安全性。 上面展示的 Model 2 体系结构是常见的“模型/视图/控制器”设计模式的服 务器端实现。这里将处理在演示和前端组件之间作了划分。演示组件是生成 HTML/XML 响应的 JSP 页,负责在浏览器呈现用户界面时确定用户界面。前端组 件(又称控制器)不处理任何演示问题,而处理所有的 HTTP 请求。这里,它们 负责创建演示组件使用的任何 bean 或对象,并根据用户的操作确定向哪个演示 组件转发请求。可以将前端组件作为 servlet 或 JSP 页来实现。 这种体系结构的优点是,演示组件本身没有处理逻辑;它只负责检索由控制器 以前创建的任何对象或 bean,并负责提取用于插入到其静态模板中的动态内容。 这样,演示与内容的清晰分离,使编程组中的开发人员和页面设计人员的角色和 责任得以清晰描绘。此方法的另一好处是,前端组件提供了单个进入应用程序的 入口点,从而使应用程序状态、安全性和演示既统一而又易于维护。 JSP 语法基础JSP 语法相当简单,可以分为指令、脚本元素和标准操作。 指令JSP 指令就是向 JSP 引擎发出的消息。它们不会直接产生任何可见的输出, 但会告诉引擎如何处理
JSP 页的其他部分。JSP 指令总是被放在 Page 指令通常,几乎在所有 JSP 页顶部都可找到 <%@ page import="java.util.*, com.foo.*" buffer="16k" %>
上面的代码将为脚本撰写提供包含的包中声明的类型,并将页面缓冲区设置 为 16K。 Include 指令
<%@ include file="copyright.html" %>
上面的指令可用于在 JSP 页中任意位置,它包含指示的文件的内容。 声明JSP 声明让您定义页面级别的变量以保存信息,或定义 JSP 页的其他部分可能 需要的支持方法。虽然盲目地让您的 JSP 页中有很多代码是一件轻松的事情,但 这样做最终会使维护成为一场噩梦。为此,同时也是为了增强可重用性,最好将逻辑密集的处理封装为 JavaBean 组件。 声明见于 <%! int i=0; %>
您还可以声明方法。例如,通过声明以下内容可以忽略 JSP 生存期中的初始 化事件: <%! public void jspInit() {
//some initialization code
}
%>
表达式利用 JSP 中的表达式,表达式求值的结果将转换为一个字符串,并会直接包 含到输出页中。通常,表达式用于通过调用
bean 的获取器方法显示简单变量值 或返回值。JSP 表达式在 <%= fooVariable %>
<%= fooBean.getName() %>
脚本小程序JSP 代码片断或脚本小程序嵌入在 <% for (int i=1; i<=4; i++) { %>
<H<%=i%>>Hello</H<%=i%>>
<% } %>
注释您总可以在 JSP 页中包含 HTML 注释,用户在查看页的源代码时可查看这些 注释。但如果您不想让用户能够查看您的注释,请将注释嵌入到
<%-- comment for server side only --%>
JSP 注释的最有用功能是,可以使用注释来有选择地禁止编译某些脚本小程序 或标记。这样,注释可以在调试和测试过程中发挥重要作用。 对象范围在讲述 JSP 语法和语义之前,理解正在处理请求的 JSP 页中 Java 对象的范 围或可见性很重要。可以隐式地使用 JSP 指令、显式地通过操作或直接使用脚本 代码(这种情况较少)来创建对象。可以将实例化的对象与一个范围属性相关联, 该范围属性定义何处有对该对象的引用以及何时移除该引用。下图指出了可以与 新创建的对象关联的各种范围: JSP 隐式对象作为一种便利的功能,JSP 容件提供一些可用在脚本小程序和表达式中的隐式 对象,而不必让页面作者先创建这些对象。这些对象用作 Servlet API 中通常定 义的基础 Java 类或接口的包装。九个隐式对象列举如下:
注意,这些隐式对象仅在系统生成的 同步问题默认情况下,JSP 页实现类的为客户端请求提供服务的服务方法是多线程的。 因此,JSP 页作者应负责确保有效地同步对共享状态的访问。有几种不同的方式 可以确保服务方法是线程安全的。较简便的方式是包含 JSP page 指令: <%@ page isThreadSafe="false" %> 这会使 JSP 页的实现类实现 使用这种方式的缺陷是不可缩放。如果因存在大量并发请求(影响对 servlet 实例的处理能力)而使等候队列增大,则客户端在获取响应时可能会出现严重的 延迟。 较好的方式是使用脚本小程序,在 JSP 页中显式地同步对共享对象(例如那 些拥有应用程序范围的实例)的访问: <%
synchronized (application) {
SharedObject foo = (SharedObject)
application.getAttribute("sharedObject");
foo.update(someValue);
application.setAttribute("sharedObject",foo);
}
%>
异常处理JSP 提供了一种相当优秀的处理运行时间异常的机制。虽然您可以在 JSP 页 中提供您自己的异常处理,但可能无法预见所有情况。通过利用
<%@ page isErrorPage="false" errorPage="errorHandler.jsp" %> 上面的代码通知 JSP 引擎将任何未捕获的异常转发给 JSP 页的 <%@ page isErrorPage="true" %> 这样便允许在脚本小程序内通过隐式异常对象来访问描述异常的 练习会话管理默认情况下,所有 JSP 页都参与 HTTP 会话。可以在脚本小程序内通过会话的 隐式 JSP 对象访问 <%
Foo foo = new Foo();
session.putValue("foo",foo);
%>
上面的代码在属于同一会话的所有 JSP 页和 servlet 中提供 <%
Foo myFoo = (Foo) session.getValue("foo");
%>
对 <%@ page session="false" %> 可以存储到会话中的对象的数目不受限制。但是,将大型对象放到会话中可能 会降低性能,因为这类对象会占用宝贵的堆空间。默认情况下,多数服务器将会
话对象的生存期设置为 30 分钟,不过您可以通过对会话对象调用 只要会话有效,JSP 引擎就对放置在会话中的对象保持一个活动引用。如果会 话无效或者遇到了会话超时,则其中的对象将标记为等待垃圾回收。 标准操作允许您执行复杂任务(如实例化对象,以及与 JSP 页和 servlet 之类的服务 器端资源进行通信)而不要求 Java 编码的操作。虽然在脚本小程序中使用 Java 代码可以实现同样的目的,但使用操作标记可提高您的组件的可重用性,并 可增强应用程序的易管理性。 使用 JavaBean 组件JSP 技术的组件模型基于 JavaBeans 组件体系结构。JavaBeans 组件就是遵 循定义好的设计/命名模式的 Java 对象: bean 通过将其属性声明为私有来封装 这些属性,并提供公共的访问器(获取器/设置器)方法来读取和修改它们的值。 在 JSP 页内访问 bean 之前,有必要标识该 bean 并获取对它的引用。 <jsp:useBean id="user" class="com.jguru.Person" scope="session" /> 在此示例中,
<jsp:useBean id="user" class="com.jguru.Person"
scope="session">
<%
user.setDate(DateFormat.getDateInstance(
).format(new Date()));
//etc..
%>
</jsp:useBean>
声明一个 JavaBean 组件后,可以访问它的属性来自定义它。应使用 <jsp:getProperty name="user" property="name" /> 更改 JavaBean 组件的属性要求您使用 <jsp:setProperty name="user" property="name" value="jGuru" /> 或者 <jsp:setProperty name="user" property="name" value="<%=expression %>" /> 在为处理窗体数据而开发 bean 时,您可以通过让 bean 属性的名称与窗体输 入元素的名称相匹配,来遵循一个常用的设计模式。您还需要为 bean 中的每个 属性定义相应的获取器/设置器方法。这样做的优点是,您现在可以指示 JSP 引 擎分析从属于请求对象的一部分的 HTML 窗体元素中传入的所有值,然后使用单 个语句将这些值赋给对应的 bean 属性,例如: <jsp:setProperty name="user" property="*"/> 这种运行时技巧可通过一种称为反省的过程来实现,该过程让类在接到请求时 公开其属性。这种反省由 JSP 引擎管理,通过 Java 反向机制实现。这种功能本 身可以在处理包含大量输入元素的复杂窗体时用作急救器。 如果您的 bean 属性的名称不匹配窗体的输入元素的名称,仍可以将它们显式 映射为您的属性,方法是将参数命名为: <jsp:setProperty name="user" property="address" param="parameterName" /> 练习转发请求使用 <jsp:forward page="somePage.jsp" /> 调用页面也可传递目标资源 bean 参数,方法是将这些参数放到请求中,如图 所示:
<jsp:forward page="<%= somePage %>" > <jsp:param name="name1" value="value1" /> <jsp:param name="name2" value="value2" /> </jsp:forward> 请求链是一种强大的功能,可用来有效地联合 JSP 页和 servlet 来处理 HTML 窗体,如下图所示: 试考虑下面的 JSP 页,例如 <jsp:useBean id="fBean" class="govi.FormBean" scope="request"/> <jsp:setProperty name="fBean" property="*" /> <jsp:forward page="/servlet/JSP2Servlet" />
public void doPost (HttpServletRequest request,
HttpServletResponse response) {
try {
FormBean f = (FormBean) request.getAttribute
("fBean");
f.setName("Mogambo");
// do whatever else necessary
getServletConfig().getServletContext().
getRequestDispatcher("/jsp/Bean2.jsp").
forward(request, response);
} catch (Exception ex) {
. . .
}
}
JSP 页 <html> <body> <jsp:useBean id="fBean" class="govi.FormBean" scope="request"/> <jsp:getProperty name="fBean" property="name" /> </body> </html> 包含请求
例如: <jsp:include page="shoppingcart.jsp" flush="true"/> 上面的代码不仅允许 Web 站点下面的站点拥有产品信息以及有关 JSP 和 servlet 的白皮书: 文档和规范Sun Microsystems 的 Java 技术站点 包含一个“产品 & API”页, 该页列出了与企业相关的产品和 API。其中一些与 JSP 相关的产品和 API 列举 如下:
佳文刊登下面是一些关于 JSP 计算技术的文章:
版权所有 1996-2000 jGuru.com。保 留所有权利。
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||