Java EE 5 开发指南 - 第四章 JavaServer Page技术

来源:互联网 发布:高中国外费用知乎 编辑:程序博客网 时间:2024/06/06 04:24

 

第四章 JavaServer Page技术

 

使用JavaServer Page(JSP)技术可以更加容易地创建包含静态与动态内容的Web构件。JSP具有servlet的所有动态处理功能,此外还提供了更接近于静态内容的创建方式。JSP的主要特点如下:

l  JSP开发语言,基于文本的描述语言,描述如何处理请求与创建响应内容

l  用于访问服务端对象的表达式语言

l  JSP语言的可扩展机制

JSP技术规范中还包含了用于开发Web容器的API,这些内容不包含在本指南中。

 

什么是JSP页面

JSP页面中包含两种内容,一种是静态的文本数据,如HTMLXMLSVGWML等,另一种是JSP元素,用于创建动态内容。

推荐使用的JSP文件扩展是“.jsp”。用于被其它页面包含的JSP页面或JSP片断文件推荐使用“.jspf”扩展名。

JSP页面中的动态元素可以使用两种格式编写:标准语法与XML语法,但一个JSP页面中只能使用一种语法,而不混用。使用XML语法编写的JSP页面其本身就是一个XML文档,可以被其它的XML工具所处理。本章以及第6章到第8章都使用的是标准的JSP语法。XML语法将在第5章中介绍。

简单示例

下图是一个JSP页面,功能是可以选择一个位置,并使用相应的格式显示日期。

上述示例被安装在<INSTALL>/javaeetutorial5/examples/web/date/目录下,其中index.jsp页面用于创建一个表单(Form),这个页面是一个典型的混合使用了静态内容与动态内容的页面。如果你以前做过Web页面,你会发现其中有你所熟悉的HTML标记内容。

在下面的代码中使用了几种JSP动态元素类型:

l  页面指令(<%@page... %>),用于设置页面内容的类型

l  标签库指令(<%@taglib... %>),导入自定义标签库

l  Jsp:userBean,创建一个服务端对象,并为其指定一个标识

l  JSP表达式语言所编写的标签式(${}),用于获取某个对象的属性,得到的值可用于设置标签属性和创建动态内容。

l  自定义标签,包括:设置变量(c:set)、迭代循环(c:forEach)、条件判断(c:ifc:choosec:whenc:otherwise

l  Jsp:setProperty,用于设置对象属性

l  判断相等的函数(f:equals),用于测试某两个事物是否相等,注意,通用使用内置的“==”运算符来判断相等。

下面是JSP页面的示例:

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core"

    prefix="c" %>

<%@ taglib uri="/functions" prefix="f" %>

<html>

<head><title>Localized Dates</title></head>

<body bgcolor="white">

<jsp:useBean id="locales" scope="application"

  class="mypkg.MyLocales"/>

 

<form name="localeForm" action="index.jsp" method="post">

<c:set var="selectedLocaleString" value="${param.locale}" />

<c:set var="selectedFlag"

  value="${!empty selectedLocaleString}" />

<b>Locale:</b>

<select name=locale>

<c:forEach var="localeString" items="${locales.localeNames}" >

<c:choose>

  <c:when test="${selectedFlag}">

    <c:choose>

      <c:when

        test="${f:equals(selectedLocaleString,

          localeString)}" >

        <option selected>${localeString}</option>

      </c:when>

      <c:otherwise>

        <option>${localeString}</option>

      </c:otherwise>

    </c:choose>

  </c:when>

  <c:otherwise>

    <option>${localeString}</option>

  </c:otherwise>

</c:choose>

</c:forEach>

</select>

<input type="submit" name="Submit" value="Get Date">

</form>

 

<c:if test="${selectedFlag}" >

  <jsp:setProperty name="locales"

    property="selectedLocaleString"

    value="${selectedLocaleString}" />

  <jsp:useBean id="date" class="mypkg.MyDate"/>

  <jsp:setProperty name="date" property="locale"

    value="${locales.selectedLocale}"/>

  <b>Date: </b>${date.date}

</c:if>

</body>

</html>

 

JSP示例程序

为了展示JSP技术,本章重写了前面的Duke书店的示例程序。对应如下表:

功能

JSP页面

进入书店

Bookstore.jsp

创建横幅

Banner.jsp

浏览图书目录

Bookcatalog.jsp

添加一本书到购物车

Bookcatalog.jsp bookdatails.jsp

查看指定图书的详细信息

Bookdetails.jsp

显示购物车

Bookshowcart.jsp

移除购物车中的购物项

Bookshowcart.jsp

购买购物车中的书籍

Bookcashier.jsp

显示购买感谢信息

Bookreceipt.jsp

 

示例中的数据仍然存放于数据库中,使用com.sun.bookstore2.database.BookDBAO访问数据库,此外,在JSP页面中通过com.sun.bookstore2.databse.BookDB组件来访问BookDBAOBookDB是被设置为一个JavaBeans组件。

BookDB的实现如下,在Bean中存在两个实例变量,一是当前书本编号,另一个是数据访问对象。

package database;

public class BookDB {

  private String bookId = "0";

  private BookDBAO database = null;

 

  public BookDB () throws Exception {

  }

  public void setBookId(String bookId) {

    this.bookId = bookId;

  }

  public void setDatabase(BookDAO database) {

    this.database = database;

  }

  public Book getBook()

    throws Exception {

    return (Book)database.getBook(bookId);

  }

  ...

}

 

这个版本的书店示例使用了MVCModel-View-Controller)架构进行设计。MVC架构是一种被广泛使用的设计架构,其设计目的是降低对象之间的偶合度,将程序的不同功能分布在一些相互的对象中。使用MVC架构,程序将分为三层:模型层(Model)、视图层(View)、控制层(Controller)。每一层完成特定的任务并与其它层提供支持:

l  模型层,用于表现业务数据和逻辑,以及操作数据的访问与修改。当模型中的数据发生变化时将会通知视图层。模型也允许控制层访问封装在模型内的业务逻辑功能。在Duke书店的示例中,购物车与数据访问对象扮演了模型层的角色。

l  视图层,用于展现模型中的数据。视图从模型是获取到数据并以相应的格式显示出来。当模型发生变化时,视图则更新其显示的内容。视图还负责向控制层传递用户的输入。在书店示例中的JSP就是处于视图层,它们将购物车与其它的数据以相应的格式显示出来。

l  控制层,用于定义程序的行为。控制层负责分配用户的请求,选择合适的视图来展现数据。还负责解释用户的输入,并映射到相应的动作上交给模型去执行。在Web程序中,用户的输入体现为相应的HTTPGET/POST)请求。在书店示例中,名为Despatcherservlet扮演了控制层的角色,它负责创建和初始化会话中的购物车对象,并分配相应的JSP页面以显示用户的请求。

注意,在Web开发中,MVC架构常被称为Model-2架构。在第3章所述的书店示例中,将业务逻辑与数据展现混合在一起,这种模式称为Model-1架构。通常,在Web开发中,推荐使用Model-2架构。

此外,在这个版本的书店示例中,还使用了几个JSTLJavaServer Pages Standard Tag Library)中的自定义标签,这些内容将在第6章中讲述:

l  用户程序流程控制的c:if, c:choose, c:when, c:otherwise

l  设置区域变量值的c:set

l  URL编码功能的c:url

l  提供本地化信息、数字、日期的fmt:message,fmt:formatNumber, fmt:formatDate

自定义标签是JSP中首先的功能实现方式,可用于非常多的动态处理任务,比如访问数据库;使用邮件、目录等企业级服务;执行程序流程控制等。在JSP的早期版本中,这些功能需要结合JavaBeans组件与脚本元素来实现,虽然这些技术在JSP 2.0中仍然有效,但使用这种技术会使得数据展现与业务逻辑混合在一起,而自定义标签技术则可以很好地实现分离。自定义标签将在后面的章节中具体讲述。

最后,这个版本的示例中还包含了一个用于在横幅上显示时钟的applet程序。实现方法见后叙内容。

Bookstore2示例中的部署描述符包含以下内容:

l  应用程序名称

l  在上下文参数中指定了绑定资源信息

l  监听器配置

l  Servlet配置信息

l  Servlet映射信息

l  嵌套的jsp-configjsp-property-group元素,用于指定被包含在每个页面中的前置与后置内容。

下图显示了示例中的bookcatalog.jsp页面,该页面显示了在售的图书列表。如果你在运行时发现了乱码情况,请确认描述符中是否正确配置了绑定资源的名称

 

JSP页面的生命周期

因为JSP技术是基于Servlet技术的,所以JSP的生命周期与动态处理能力与Servlet类似。

当某个请求被传递到JSP页面时,Web容器首先检查JSP页面类是否比JSP页面要旧,如果是的话,Web容器将JSP页面转换为相应的servlet类,并编译该类。在开发时,JSPservlet的一个优势就是编译操作是自动完成的。

转换与编译

在转换阶段,JSP页面中不同的数据将会进行不同的处理。静态数据将被直接通过response输出,而其它JSP元素将进行如下的转换:

l  页面指令用于控制Web容器如何转换和执行JSP页面

l  脚本元素被插入到JSP页面类中

l  EL(表达式语言)表达式被转换为参数传递给表达式求值程序

l  Jset:[set|get]Property元素被转换为调用相应的JavaBean的方法

l  Jsp:[include|forward]元素被转换为相应的servlet API

l  Jsp:plugin元素被转换为浏览器所能识别的标记

l  自定义标签被转换为自定义标签库中处理程序的调用

AS服务器中,转换后的类文件的路径类似于:

<JAVAEE_HOME>/domains/domain1/generated/jsp/j2ee-modules/WAR_NAME/pageName_jsp.java

其中WAR_NAME是应应程序的名称,pageNameJSP文件名称。

在转换和编译阶段都有可能会发生错误。当JSP第一次被请求时,容器进行JSP页面的转换和编译,如果在这两上阶段中出现错误,服务器将返回JasperException,在错误信息还包含了JSP页面的名称和错误发生行地址。

在正确转换和编译之后,JSP页面servlet类的生命周期与普通servlet几乎完成相同:

l  如果页面类尚未实例化,那么容器将载入页面类、实例化类对象、调用对象的jspInit方法

l  容器调用_jspService方法,并传递请求与响应对象

l  如果窗口需要移除页面类的实例,移除之前会调用对象的sjpDestroy方法

执行

可以通过页面指令控制很多的页面执行参数,接下来我们将介绍与输出缓存与错误处理相关的页面指令,其它的页面指令将在后绪的章节中介绍。

缓存

JSP页面被执行时,通过response输出的内容会自动地缓存,可以通过如下的页面指定调置缓冲区大小:

<%@ page buffer="none|xxxkb" %>

设置大一些的缓存允许在将数据发送给客户端之前有更多的时间设置页面状态、设置头信息、或转向到其它页面。而小点的缓存则会降低服务端的内存占用量,并且客户能够更快整地得到返回内容。

错误处理

JSP执行时有可能会发生各种各样的错误,使用如下的页面指令可以告诉服务器当有错误发生时转换到某个特定的错误页面:

<%@ page errorPage="file_name" %>

Duke书店示例中的preludeerrorpage.jspf页面中包含如下的指令:

<%@ page errorPage="errorpage.jsp"%>

errorpage.jsp页面中包含如下的指令,用于指定当前页面是一个错误处理页面:

<%@ page isErrorPage="true" %>

这个页面指令在页面中生成了一个javax.servlet.jsp.ErrorData对象,通过它可以获得与错误相关的信息。你可以使用EL表达式访问错误对象,如“${pageContext.errorData.statusCode}”获取错误代码,“${pageContext.errorData.throwable}”获取错误异常对象。你也可以通过${pageContext.errorData.throwable.cause}获取异常原因。

以下是在Duke书店中的错误页面代码:

<%@ page isErrorPage="true" %>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core"

  prefix="c" %>

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"

  prefix="fmt" %>

<html>

<head>

<title><fmt:message key="ServerError"/></title>

</head>

<body bgcolor="white">

<h3>

<fmt:message key="ServerError"/>

</h3>

<p>

: ${pageContext.errorData.throwable.cause}

</body>

</html>

 

注意,在应用程序的部署描述符中也可以指定错误处理页面,如果同时在页面中也指定了错误页面,那么页面中的设置将会优先生效。

 

创建静态内容

你可以在JSP页面中像编写静态页面一样输入静态内容。静态内容中基于文本格式的HTMLWML、或XML。默认的格式是HTML,如果希望使用HTML以外的其它格式,需要在JSP页面开始的位置包含一个contentType页面指令,该指定的作用是告诉浏览器如何理解页面的输入内容。例如,如果希望页面被浏览器按照WML格式理解,需要在页面中包含如下指令:

<%@ page contentType="text/vnd.wap.wml"%>

可用的内容类型可以在IANA网站(http://www.iana.org/assignments/media-types/)查询得到。

页面输出编码

使用contentType指定还要可以设置页面输出内容所使用的字符集编码。例如,如果页面使用UTF-8编码输出内容,则在页面中可以包含如下的指令:

<%@ page contentType="text/html; charset=UTF-8" %>

如果不设置编码设置,有可能会引日期格式不正确的情况。

使用如下指令,也可单独设置页面输出编码:

<%@ page pageEncoding="UTF-8" %>

此外,也可以在部署描述符中的JSP属性组中整组设置JSP页面使用的编码格式。

 

创建动态内容

JSP页面中可以使用Java程序语言中的对象属性生成动态内容。

JSP中使用对象

JSP页面中可以访问很多对象,包括企业BeanJavaBean组件等。JSP中包含了一些隐含有效的对象,也可以在页面创建其它的特定的对象。

使用隐含对象

隐含对象是由容器创建的,包含了一些特定用途的对象,如是请求、页面、会话、应用程序等。其中很多是在JSP所基于的技术——Servlet——中已经讲述过的。在后面我们将介绍如何在JSP中使用这些对象。

使用自定义对象

如果可能,程序中的行为功能应该封装在一个个的对象中,这样页面设计人员可以集中注意在如何展现上面。对象的创建可以由熟悉Java语言、数据库技术、或其它服务的编程人员来完成。使用自定义对象的主要方法是在JSP中利用JSP标签创建JavaBeans组件及设置其属性,或者使用EL表达式访问它们的属性。也可以在脚本元素中使用JavaBeans组件及其它对象。

使用共享对象

通常,JSP页面的执行类似于多线程下的servlet,也会涉及到共享对象的访问限制。可以使用如下的页面指令设置页面不使用多线程执行:

<%@ page isThreadSafe="true|false" %>

isThreadSafe设置为true时,Web容器可以分配多个纯程执行页面,以响应多个请求。这是默认设置。此时,应该确保能够恰当地访问在页面中共享的对象,包括页面中定义的和声明的JavaBean组件,以及页面中使用的隐含对象的属性。

如果isThreadSafe设置为false,容器将保证在任何时刻只会存在一个JSP运行实例,用其处理所有的用户请求,以及访问页面组的对象。但是,此刻仍然要保证对于其它隐含的共享对象中的同步访问。

当页面将isThreadSafe设置为false时,JSP的生成类将实现javax.servlet.SingleThreadModel接口,在新版本的Servlet 2.4中,不推荐该接口。

 

统一表达式语言

JSP 2.1中引用的主要的新特征是统一表达式语言,结合了JSP 2.0中的表达式语言与JSF技术中的表达式语言。

JSP 2.0中的表达式语言为页面编写人员提供了使用简单的表达式动态读取JavaBeans组件的方法。例如,在下面代码中,条件判断标签中的test属性使用了EL表达式,判断session域中的cart对象的numberOfItem属性是否大于零。

<c:if test="${sessionScope.cart.numberOfItems > 0}">  

  ...

</c:if>

就像在JSP生命周期一节中所述,JSP支持简单的请求/响应模式,当一个页面被执行时,HTML格式的标记被立即返回,因此,JSP 2.0所提出的简单的只读EL表达式能够满足应用的需求。

此外,JSFJavaServer Faces)技术提供了一套多变的生命周期模型,以适应丰富的UI组件,提供了数据转换、验证、传递数据、及事件处理的功能。为了实现这些功能,JSF引用了其自己的表达式语言,包含如下功能:

l  延期计算表达式

l  存值、取值功能

l  调用方法功能

JSF中使用统一表达式的方法在后面的章节中再介绍。

将两种表达式进行统一的原因有两个,一个原因是页面编写人员要以混用JSP内容与JSF内容,无需担心因为生命周期不同而引起的冲突。另一个原因是其它基于JSP的技术可以像在JSF中一样地使用附加的功能。事实上,JSP标签与静态内容仅在JSP 2.0中被继续使用,JSP自定义标签的作者可以创建利用统一表达式语言新特性的标签。

总结一下,使用新的统一表达式语言允许我们在页面中使用简单的表达式执行以下工作:

l  动态读取存放于JavaBeans组件中的数据,支持多种数据格式,支持隐含对象。

l  动态写入数据到JavaBeans中,如用户输入在表单中的数据

l  调用任意的公共的静态方法

l  动态执行算述操作

统一EL表达式还允许自定义标签开发人员指定标签属性可以接受以下的某种表达式:

l  立即计算表达式与延迟计算表达式。立即计算表达式是由JSP引擎立即求值的表达式,而延迟计算表达式可以由底层技术延期进行计算。

l  值表达式与方法表达式。值表达式用于引用数据,而方法表达式可以调用一个方法。

l  Rvalue表达式与Lvalue表达式。Rvalue表达式只能读取数据,而Lvalue表达式即可读数据也可写数据到对象中。

最后,统一表达式语言还提供了可插入的表达式解析API,开发人员可以实现自己的表达式解析器,以此来实现统一EL中未提供的功能。

本节在讲述统一EL时将包含以下内容:

l  立即计算与延迟计算表达式

l  值表达式与方法表达式

l  定义标签属性类型

l  禁用表达式计算

l  字面表达式

l  解析表达式

l  隐含对象

l  功能函数

 

立即和延迟计算语法

统一EL支持立即和延迟计算表达式。立即计算意味着当页面被展现时由JSP引擎立即计算表达式的值。延迟计算的意思是可以将计算表达式的操作放在页面生命周期的某个随便之后,具体则根据需要来确定。

立即计算表达式使用“${}”语法,这种语法来自于JSP 2.0。使用“#{}”语法定义的表达式是延迟计算表达式,这是在JSF技术中引入的。

因为JSF生命周期的多元化,JSF技术必须延迟表达式的计算。在其生命周期中,组件事件被处理、数据被验证、以及其它任务的执行,这些工作必须在特定的顺序中被执行,必须能够延迟表达式的计算,以便能够在合适的时间才执行。

在其它的技术中,可能有其它的具体原因使用延迟计算表达式的原因。

立即计算

所有使用“${}”语法表示的表达式都是立即计算表达式。可以用在模板文本或当作JSP标签的属性值。

下面的代码中就使用了一个立即计算表达式,用于计算session域中的cart对象的total属性值,并将结构赋值给标签的value属性。

<fmt:formatNumber value="${sessionScope.cart.total}"/>

JSP引擎计算表达式的值,转换它,并将结果传递给标签的处理器。

立即计算表达式是只读的。上面的代码中只能够获取total的值,不可以设置值。

延迟计算

延迟计算表达式使用“#{}”语法,无论使用的是何技术,根据定义可以在页面的其它生命周期执行。例如在JSF中,控制器可以在不同的阶段执行表示式求值,具体要看表达式在页面中被如何使用。

如是下的代码是在JSF页面中使用inputText标签,提供了一个文本域给用户输入数据。在标签中的value属性中使用了延迟表达式指向了customer对象的name属性。

<h:inputText id="name" value="#{customer.name}" />

在第一次的初始请求中,当输出响应内容阶段,JSF计算表达式“#{coustomer.name}”的值。在这个阶段,表达式仅仅访问customer对象的name值,此时与立即计算表达式并无区别。

当页面回调用时,JSF处理另一个生命周期的阶段,此时处理表达式的方式是从请求中获取用户的输入,验证之后将其传递给customer对象。

如上所述,延迟计算表达式可能是可读可写的值表达式。此外也可以是方法表达式。值表达式(立即与延迟)与方法表达式将在下一节介绍。

 

值表达式与方法表达式

统一表达式语言定义了两种表达式:值表达式与方法表达式。值表达式可以获取或设置一个值。方法表达式可以调用一个方法并返回一个值。

值表达式

值表达式进一步可以分为左值表达式与右值表达式。右值表达式可以读取值,但不能写入值。左值表达式可以读取也可以写入值。

使用“${}”语法定义的立即计算表达式都是右值表达式,使用“#{}”定义的延迟计算表达式可以是左值表达式也可以是右值表达式。考虑下面的两个表达式:

<taglib:tag value="${customer.name}" />

<taglib:tag value="#{customer.name}" />

前者使用立即计算语法,而后者使用的是延迟计算语法。第一个表达式访问customer对晚有的name属性,并输出在页面中。第二个表达式也做了同样的事情,然而标签处理程序可以将表达式的计算工作延迟在页面的某个生命周期阶段中执行。

例如在JSF技术中,第二个表达式会在页面初始请求时立即计算并返回结果在页面中,此时,表达式扮演着右值表达式的角色。而在页面回调阶段,customer对象的name属性被调置为用户所输入的内容,此时表达式是左值表达式。

 

使用值表达式引用对象

左值和右值都可以引用以下的对象和属性:

l  JavaBean组件

l  集合

l  Java枚举类型

l  隐含对象

JSP中可用的隐含对象请参考隐含对象一节。

可以在表达式中使用变量名称引用这些对象。以下表达式引用了一个名为customerJavaBean组件。

${customer}

容器依照PageContext.findAttribute(String)的方式查寻计算表达式中变量的值,其中String型的参数是变量的名称。例如,当计算“${customer}”的值时,容器将会在页面、请求、会话、及程序集中查找customer对象并返回它的值。如果未找到customer将会返回null值。如果变量与隐含对象重名的话,容器将返回隐含对象,而不是要引用的变量。

你可以通过自定义EL解析器改变获取变量的方式,这是统一表达式语言的新特性。例如,你可以提供一个EL解析器截取customer对象,所以“${customer}”将返回EL解析器中的某个值。但是,不能通过这种方式修改隐含对象的获取方式。

在表达式中引用枚举项需要使用文字字面量的方式。例如,如下的枚举类:

public enum Suit {hearts, spades, diamonds, clubs}

假如要引用Suit中的Suit.hearts项,可以使用“hearts”字面量。根据上下文,字面量将会自动转换为相应的枚举类型。例如,在下面的表达式中使用的mySuit是一个Suit类型实例,“hearts”字面量将在比较前转换为Suit.hearts枚举项。

${mySuit == “hearts”}

使用值表达式引用对象属性

可以使用“.”或“[]”标注引用bean对象的属性、枚举实例、集合项、或隐含对象的属性,这点类似于ECMAScript脚本。

例如,如果希望引用customer对象的name属性,可以使用“$[customer.name]”或“${customer[“name”]}”。中括号中的字符串是要引用的属性名称。

可以使用双引号或单引号标注字符串属性名称。你也可以组合使用“.”与“[]”符号,如:

${customer.address[“street”]}

枚举型属性也可以通过这种方法引用。可是,与JavaBean属性一样,枚举属性也要遵守JavaBean组件的约定。这意味着它也必须具有get<Property>方法,以下来访问属性的值。其中<property>是属性的名称。

例如,如果有一个枚举列出了我们星系的行星名称,可以使用如下的表达式引用getMass方法:

${myPlanet.mass}

如果要访问的是数组中的某一项,必须使用能够被转换为整数的字面量或整数型的引用变量。以下的两个表达式可以引用同一样数组元素,其中socks可以被转换为整数:

${customer.orders[1]}

${customer.orders.socks}

类似地,如果访问的是Map中的某项则不需要转换:

${customer.orders[“socks”]}

右值表达式还可以直接引用值,而不是某个对象,该植也可以是由某个运算所得到的结果,或者是某个字面值,例如如下:

${“literal”}

${scustomer.age + 20}

${true}

${57}

统一表达式语言定义了以下几种字面量:

l  布尔:truefalse

l  整数

l  浮点数

l  字符串:使用单引号或双引号标注

l  空值:null

你也可以编写使用枚举项操作的表达式。例如,使用如下的枚举:

Public enum Suit{club,diamond, heart, spade}

在定义枚举型变量mySuit之后,你可以编写下列表达式测试mySuit是否是spade

${mySuit == “spade”}

当表达式解析器解析这个表达式时,将会调用枚举的valueof方法,将spade字符串转换为枚举型,如:

mySuit.valueof(Suit.class, “spade”)

有关使用表达式引用JavaBean组件的内容请参考JavaBean组件一节。‘

值表达式可以用在哪里

使用“${}”定义的表达式可以用在以下一些地方:

l  静态文字

l  可接受表达式的任何标准与自定义标签属性中

在静态文本中的表达式将被计算出结果并插入到当前输出中。以下是一个在静态文本中的例子:

<some:tag>

Some text ${expr} some text

</some:tag>

如果静态文本出现在标签体中,如果标签体被定义为依赖的(tagdependent),那么表达式将不同被计算。

左值表达式只能够使用在可以接受表达式的标签属性中。

以下是设置标签属性值的三种方法,无论使用的是左值还是右值表达式:

l  使用单一的表达式结束:

<some:tag value=”${expr}”/>

<another:tag value=”#{expr}”/>

这些表达式被计算得到结果,并将结果转换为属性接收的数据类型。

l  分隔或环绕使用一个或多个表达式:

<some:tag value=”some${expr}${expr}text${expr}”/>

<another:tag value=”some#{expr}#{expr}text#{expr}”/>

这种表达式称为组合表达式。它们将被从左至右地计算求值。每个表达式的计算结果都被转换为字符型并连接在一起,连接后的字符串将被转换为属性所接收的数据类型。

l  单独的文本

<some:tag value=”sometext”/>

这样的表达式尔之为字面量表达式。在这种情况下,字符串将被转换为相应的数据类型。字面量表达式具有特殊的语法规则。具体请查看字面量表达式相关章节。当标签属性包含一个枚举型时,表达式必须使用字面量表达式。例如,使用“hearts”表达式表示“suit.hearts”,字面量将会转换为Suit枚举类型。

所有用于设置属性值的表达式都在程序上下文中被计算。如果计算结果与属性数据类型不相同,那么数据将会被自动转换。例如,表达式“${1.2E4}”如果被设置为一个浮点型的属性,系统将进行如下转换:

Float.valueOf(“1.2E4”).floatValue()

 

方法表达式

支持迟延的方法表达式是统一表达式语言的另一个特性。方法表达式可用于调用任意的公共方法,方法可以具有返回值。这点与统一语言的功能函数特性有些类型,不同之处在下面的章节中将会讲述。

方法表达式主要是提供给JSF技术使用,但任何支持统一表达式语言的技术都可以使用。接下下,让我们看一看如何在JSF中使用方法表达式。

JSF技术中,标签组件在页面中呈现一个UI组件。标签组件中可以使用方法表达式调用方法,用以为组件进行一些相关的处理。这在进行组件的事件处理与数据验证时是非常有用的。例如:

<h:form>

  <h:inputText

    id="name"

    value="#{customer.name}"

    validator="#{customer.validateName}"/>

  <h:commandButton

    id="submit"

    action="#{customer.submit}" />

</h:form>

 

inputText标签显示了一个文本输入控件。在控制的validator属性引用了customer一个方法,名为validateName。在TLDTag Library Descriptors)中定义了inputTag标签validator属性可以接收的方法签名。Action属性引用了另一个名为submit的方法,在TLD中定义了该方法必须返回一个对象实例,用于处理当按钮点击的页面的导航。

Validation方法在页面的数据验证生命周期中被调用,而submit方法在执行阶段被调用。因为一个方法可以在页面生命周期的不同阶段被调用,所以方法必须是可延迟的,必须全延迟表达式的语法。

类似于左值表达式,方法表达式可以使用“.”与“[]”操作符。例如,表达式#{object.method}#{object[“method”]}相等。在中括号中的字面量被转换为字符串,作为方法的名称。 如果方法被找到,容器将会调用该访问并获取返回的信息。

方法表达式只可用于在标签属性中,使用方法如下:

l  在单一的表达式结构中,结合JavaBean组件,如:

<some:tag value=”#{bean.method}”/>

表达式被传递给标签处理器,接下来将会调用对象的方法。

l  仅使用文本

<some:tag value=”sometext”/>

方法表达式支持字符串常量主要是用于支持JSF中的action动作。当这个表达式被计算时,将会直接返回字面量作为方法的返回值,并转换为相应标签TLD中定义的数据类型。

定义标签属性类型

如前一节所述,所有的表达式可以用在标签的属性中。可以使用哪种表达式,表达式如何计算——立即计算还是延迟计算——都由标签的TLDTag Library Descriptors)文件来决定。

如果你计算创建一个自定义标签,你需要指定在TLD中的每个标签可以接收的表达式类型。下将显示了三种不同的类型的标签可以接受的EL表达式,并给出了表达式的示例和标签定义的示例。你不可以在动态属性(接收在运行进动态计算的值)上使用“#{}”语法。

属性类型

表达式示例

标签属性定义

动态

“literal”

${literal}

“literal”

<rtexprvalue>true</rtexprvalue>

<rtexprvalue>true</rtexprvalue>

<deferred-value>
<type>java.lang.String</type>
</deferred-value>

迟延值

#{customer.age}

“literal”

<deferred-value>
<type>int</type>
</deferred-value>

<deferred-method>
<method-signature>
java.lang.String submit()
</method-signature>
<deferred-method>

迟延方法

#{customer.calcTotl}

<deferred-method>
<method-signature>
double calcTotal(int, double)
</method-signature>
</deferred-method>

除在上表中列出的标签属性外,还可以定义即可以使用动态也可以使用延迟表达式的标签属性。既然这样,标签定义中包含一个置为true值的rtexprvalue,还包含一个deferred-valuedeferred-method定义。

 

禁用表达式计算

因为EL表达式的语法是在JSP 2.0之后引用的,很多早期版本的程序中使用了“${}”与“#{}”字符串,为了禁用EL表达式可以使用如下的方法:

l  避免使用“${}”与“#{}

l  JSP属性组中配置禁用EL表达式

l  在页面中使用页面指令禁用EL表达式

为了避开使用“${}”与“#{}”可以使用转意字符“/”:

Some text /#{some more /${text

<my:tag someAttribute=”sometext/#{more/${text”/>

禁用表达式的另一个方法是使用JSP属性组,可以通过deferred-syntax-allowed-as-literal选项配置是否允许“#{”作为普通字符串,或者使用el-ignored设置略过所有的EL表达式。

<jsp-property-group>

  <deferred-syntax-allowed-as-literal>

    true

  </deferred-syntax-allowed-as-literal>

</jsp-property-group>

<jsp-property-group>

  <el-ignored>true</el-ignored>

</jsp-property-group>

最后,你可以在页面中使用页面指令配置该页面禁用EL表达式,使用deferredSyntaxAllowedAsLiteral指令配置禁用“#{”,或使用isELIgnored彬所有EL表达式。

<%@page ... deferredSyntaxAllowedAsLiteral="true" %>

<%@ page isELIgnored ="true" %>

这些配置的有效值是truefalse。如果isELIgnoredtrue值,当页面显现时将会将EL表达式作为普通字符串处理。如果设置值为false,而属性定义中的rtexprvalue置为true或者定义为使用迟延表达式时,那表达式将被容器进行计算处理。

isELIgnored的默认设置依赖于Web部署描述符的版本,使用Servlet 2.3及早期版本的默认设置是忽略EL表达式,而Servlet 2.4及后期版本则使用EL表达式有效的选项。

字面表达式

字面表达式无需使用“${}”与“#{}”语法,字面的值将直接转换为字符串。

如果在字面表达式中使用了“${}”或“#{}”字符,那么需要使用转义字会进行转义:

l  使用组合表达式,如:

${‘${‘}exprA}

#{‘#{‘exprB}

以上表达式将被转义为“${exprA}”和“#{exprB}

l  使用转义字符“/$”和“/#”,如:

/${exprA}

/#{exprB}

以上表达式将被转义为“${exprA}”和“#{exprB}

当字面表达式被计算时,可能会被转换为其它数据类型。下表列出了可能存在的转换情况:

表达式

表达式类型

结果类型

Hi

String

Hi

True

Boolean

Boolean.TRUE

42

Int

42

字面表达式可以是立即表达式,也可是延迟表达式,即可以是值表达式,也可以是方法表达式。什么时候运行取决于使用表达式的地方。如果标签属性的定义是使用延迟的值表达式,那么将由表达式的使用位置与引用类型决定表达式在页面的哪个生命周期中被计算。

如果字面表达式被当作是方法表达式来使用,那么字面值将作为方法的返回值直接返回。面猜数字的程序中就使用了一个字面表达式提供给commandButton标签使用,用于处理页面之间的转向。

表达式解析器

统一表达式语言还提供了一个新的、用于制作表达式解析器的可插入式的API。其中主要包含如下的API

l  ValueExpression类,用于定义值表达式

l  methodExpression类,用于定义方法表达式

l  ELResolver类,定义了了表达式的解析机制

l  一组ELReover的实现类,每个类负责解析一种对象类型和属性

l  一个ELContext对象,用于保存EL表达式相关的状态信息,包含上下文对象等。

大多数开发人员都无需使用这些API,除非是打算编写自己的EL表达式解析器。那些开发JSF自定义组件的开发人员需要使用ValueExpressionMethodExpression

表达式的计算流程

当一个页面中的值表达式在页面初次请求时被激活,将会创建一个ValueExpression对象用于表示该表达式。接下来,ValueExpression对象中的getValue方法被调用。这个方法将会依次调用相关解析器的getValue方法。在页面回调期间,左值表达式的setValue的调用也存在类似的情况。

如果是一个方法表达式,BeanELResolver负责找出实现被调用的方法的对象。类似于计算值表达式,当遇到一个方法表达式时,一个MethodExpression对象将被创建,随后,调用该方法或者执行methodExpression对象上的getMethodInfo。在方法中依次调用BeanELResolver对象的getValue方法。getMethodInfo通常被一些工具所使用。

当一个解析器完成了表达式的处理之后,将会设置ELContext对象中的Resolved属性为true值,以表示该表达式已处理完毕,无需再调用其它解析器。

 

隐含对象

JSP中定义了一些隐含对象,包含:

l  pageContext:页面上下文,提供了访问多种对象的途径,如下:

l  servletContext:页面servlet类的程序上下文,其中包含了一些应用程序相关的信息

l  session:客户端的会话对象

l  reqeust:请求对象

l  response;输出对象

此外,还有几个隐含对象便于我们访问以下对象:

l  param:请求参数的映射表,对参数的一个值

l  paramValues:请求参数的映射表,对应参数的多个值

l  header:请求头信息的映射表,对应头信息中的一个值

l  hreaderValues:请求头信息的映射表,对应头信息中的多个值

l  cookie:映射客户端cookie名称和值

l  initParam:映射程序上下文中的初始化参数名称与值

最后,还有以下一些域对象:

l  pageScope:页面域中的变量名称与值的映射

l  reqeustScope:请求域中的变量名称与值的映射

l  sessionScope:会话域中的变量名称与值的映射

l  applicationScope:应用程序域中的变量名称与值的映射

JSP 2.1中提供了两个EL解析器来处理引用了隐含域的表达式:ImplicitObjectELResolverScopedAttributeELResolver

当一个名称与某个隐含对象相匹配时,表达式将会交给implicitObjectResolver来执行,并返回相匹配的隐含对象。解析器的处理是可能为null的,意思是下面的表达式将会返回sessionScope隐含对象,一旦隐含对象被找到,MapELResolver将会再去寻找profile的属性。

${sessionScope.profile}

ScopedAttributeELResolver计算一个存储在域中的简单的对象。象ImplicitObjecteLResolver一样,也是基于空值的方式进行计算。这个解析器将在所有域中依照PageContext.findAttribute(String)的方式查找相匹配的对象,直到找到为止。例如,在计算表达式${product}时,解析器将查看pagerequestsession、与application域,找到product对象后返回其值。如果没找到,将会返回null值。

当表达式引用的对象与隐含对象同名时,返回的将是相应的隐含对象,而不是查找到的域中的对象。例如,${pageContext}表达式返回的是隐含对象PageContext,而不是某个域中保存的pageContext对象。

 

操作符

除了“.”与“[]”操作符外,JSP表达式语言还提供了以下操作符,这些操作符只能够被用于右值表达式:

l  算术运算符:+,-(二元),*,/div,%mod,-(一元)

l  逻辑运算符:and,&&,or,||,not,!

l  关系运算符:==,eq,!=,ne,<,lt,>,gt,<=,ge,>=,le。比较运算符可以在boolean,string,integer,float型字面量之间进行。

l  Emptyempty运算符是一个前缀运算符,可用于判断某个值是否为空。

l  条件判断运算符:A ? B : C,根据A值的真假选择返回B或者C

操作符的优先级如下:

l  [] .

l  () – 用于改变运算符的运算顺序

-      (一元) not ! empty

l  / div % mod

l  + - (二元)

l  < > <= >= lt gt le ge

l  == != eq ne

l  && and

l  || or

l  ? :

保留的关键字

下列关键字由JSP表达式语言内部使用,注意不要用它作为自定义的名称:

and   eq   gt   true   instanceof

or    ne   le   false  empty

not   lt   ge   null   div   mod

注意,其中有一些关键字目前并未使用,保留供以后使用。

示例

下表中包含了一些表达式及其运行的结果

表达式语言

运算结果

${1 > (4/2)}

False

${4.0 >= 3}

True

${100.0 == 100}

True

${(10*10) ne 100}

False

${‘a’ < ‘b’}

True

${‘hip’ gt ‘hit’}

False

${4 > 3}

True

${1.2E4 + 1.4}

12001.4

${3 div 4}

0.75

${10 mod 4}

2

${!empty param.Add}

如果在请求参数中包含有Add参数并且不为空则返回True

${pageContext.request.contextPath}

返回上下文路径

${sessionScope.cart.numberOfItems}

返回session域中的cart对象的numberOfItems属性

${param[‘mycom.productId’]}

在请求参数中名为mycom.productId的变量值

${header[“host”]}

返回主机头信息

${departments[deptName]}

返回departments映射表中的名为deptName属性

${reqeustScope[‘javax.servlet.forward.servlet_path’]}

返回请求参数域中的名为javax.servlet.forward.servlet_path的属性

#{customer.lName}

在初始化请求时返回customer对象中的lName属性。在页面回调时设置lName的值

#{customer.calcTotal}

返回customer对象的calcTotal方法的返回值

 

函数

JSP表达式语言规范中允许我们定义一个函数在表达式中调用。函数的定义使用的是与自定义标签相同的机制。

乍一看,函数与方法的表达式非常相似,但它们的不同之处有如下几处:

l  函数使用的是静态方法,而方法表达式使用的不是静态的,而是任意公共的方法。

l  函数是在页面编译时进行标识,而方法表达式是在运行时动态解析

l  函数的参数与调用格式是在EL表达式中定义。而方法表达式只需标注方法。方法表达式无需在EL表达式是标明它的签明,而是由标签属性中定义。

使用函数

函数调用可以出现在静态文本与标签属性中。

JSP页面中使用函数,首先要用页面指令导入包含函数的标签库。然后使用指令中定义的前缀来调用函数。

例如,在日期示例中的index.jsp导入了/functions库,并调用了其中的equals函数:

<%@ taglib prefix="f" uri="/functions"%>

...

    <c:when

      test="${f:equals(selectedLocaleString,

        localeString)}" >

在这个示例中,表达式引用的函数使用的是立即计算语法。如果标签属性可以接收的话,页面作者还可以使用延迟语法引用函数。

如果使用延迟语法引用了一个函数,那么在页面展现时不会立即调用该函数,而由其底层技术决定在何时调用。

定义函数

要定义一个函数,首先要在一个公共类中定义该函数为静态公共的。日期示例中的Mypkg.MyLocales类中就定义了一个用于判断两个字符串是否相等的函数:

package mypkg;

public class MyLocales {

 

  ...

  public static boolean equals( String l1, String l2 ) {

    return l1.equals(l2);

  }

}

然后,需要在TLD中声明要使用的方法。下面的functions.tld文件是日期示例中的声明文件,将上面定义的函数映射为equals函数,如下:

<function>

  <name>equals</name>

  <function-class>mypkg.MyLocales</function-class>

  <function-signature>boolean equals( java.lang.String,

    java.lang.String )</function-signature>

</function>

在一个标签库中不能出现两个同名的函数。

 

JavaBeans组件

JavaBeans组件是可以重用和组合的Java类,任何符合特定的设计约定的Java类都可以是JavaBeans组件。

JSP技术支持通过标准JSP语言元素使用JavaBean组件,你可以很容易地创建和初始化bean和获取/设置对象的属性。

JavaBeans组件的设计约定

JavaBeans组件的设计约定包括类对象中的属性的实现,及访问属性的相关的公共方法的实现。

一个JavaBeans组件属性可以是:

l  可读/可写,只读,或只写

l  简单的,只包含一个值的属性,也可以是包含代表了一个数组的属性

一个属性不是一定要与一个实例变量相对应。属性必须是公共的,可以在被外界方便地调用,并且符合以下约定:

l  每个可读的属性,必须有一个如下的方法:

PropertyClass getProperty() { ... }

l  每个可写的方法,必须有一个如下的方法:

setProperty(PropertyClass pc) { ... }

除了属性方法外,一个JavaBeans组件必须定义一个无参数的构造函数。

在书店示例中,bookstore.jsp,bookdetails.jsp,catalog.jsp,showcart.jsp使用了下列的JavaBeans组件:

l  com.sun.bookstore2.database.BookDB

l  com.sun.bookstore2.database.Bookdetails

BookDB提供了一个用于访问BookDBAO对象的JavaBeans组件。Showcart.jsp页面与cashier.jsp页面访问com.sun.bookstore.cart.ShoppingCart,这个Bean表示一个用户的购物车。

BookDB中包含两个可写的属性,bookIddatabase,还包含了三个可读的属性,bookDetails,numberOfBooks,books,这三个属性都没有对应某个实例变量,而是bookIddatabase属性相关的功能函数。

package database;

public class BookDB {

  private String bookId = "0";

  private BookDBAO database = null;

  public BookDB () {

  }

  public void setBookId(String bookId) {

  this.bookId = bookId;

  }

  public void setDatabase(BookDBAO database) {

  this.database = database;

  }

  public Book getBook() throws

    BookNotFoundException {

    return (Book)database.getBook(bookId);

  }

  public List getBooks() throws BooksNotFoundException {

    return database.getBooks();

  }

  public void buyBooks(ShoppingCart cart)

    throws OrderException {

    database.buyBooks(cart);

  }

  public int getNumberOfBooks() throws BooksNotFoundException {

    return database.getNumberOfBooks();

  }

}

 

创建和使用JavaBeans组件

为了在页面中声明使用JavaBeans组件,可以使用jsp:useBean元素。有如下两种形式:

<jsp:useBean id="beanName"

  class="fully_qualified_classname" scope="scope"/>

<jsp:useBean id="beanName"

  class="fully_qualified_classname" scope="scope">

  <jsp:setProperty .../>

</jsp:useBean>

当希望在声明中包含jsp:setProperty时使用第二种形式,这种形式将在下一章节中使用。

Jsp:useBean元素声明页面将会使用一个存放于指定域中的bean对象,这个域可能是application,session,request,page域。如果这个bean不存在,声明将会创建在相应的域中创建该对象。标签中的id属性用于标识该对象的名称,用于在EL表达式、其它JSP元素、页面脚本元素中引用。Class属性指出该对象的类型,必须是一个完成的类名。注意,bean对象的类不能处于未命名的包中,所以类名格式必须是package_name.class_name

下列的页面元素将创建一个mypkg.myLocales实例,并将其存放于application域中,在应用程序的其它的地方都可以通过名称locales访问该对象。

<jsp:useBean id="locales" scope="application" class="mypkg.MyLocales"/>

 

设置JavaBeans组件的属性

JSP页面中设置JavaBeans组件属性的标签方法是通过jsp:setProperty元素。其语法决定于要设置的属性值的来源。下表汇总了一些使用方法:

属性值来源

语法

文字面量

<jsp:setProperty name="beanName"   property="propName" value="string constant"/>

请求参数

<jsp:setProperty name="beanName"   property="propName" param="paramName"/>

名称相同的请求参数

<jsp:setProperty name="beanName"   property="propName"/>  

<jsp:setProperty name="beanName"   property="*"/>

表达式

<jsp:setProperty name="beanName"   property="propName" value="expression"/>  

<jsp:setProperty name="beanName"   property="propName" >

  <jsp:attribute name="value">

    expression

  </jsp:attribute>

</jsp:setProperty>

以上语法中:

l  beanName必须与事前使用userBean定义的对象名称相同

l  JavaBeans组件上必须有相应的setPropName方法

l  paramName必须是一个请求参数的名称

 

无论是使用字面量还是使用请求参数的方式进行属性设置,属性值都必须是在下表中所列出的类型中的某一种。因为字面量与请求参数都是字符串,所以Web容器将会自动转换为属性相应的数据类型,转换的方式在下面中列出。

属性值也可能是一个类对象的名称,此时,setAsText(String)方法将会被调用。在方法中如果出现IllegalArgumentException异常那么转换将会失败。

也可以使用表达式为某个属性赋值。表达式返回值的类型必须与属性类型相匹配或者能够转换为目标类型。

属性类型

字符串值的转换

Bean对象属性

使用setAsText(string-literal)

Boolean

调用java.lang.Boolean.valueOf(String)

Byte

调用java.lang.Byte.valueOf(String)

Charcharacter

调用java.lang.String.charAt(0)

Double

调用java.langDouble.valueOf(String)

Intinteger

调用java.lang.Integer.valueOf(String)

Float

调用java.lang.Float.valueOf(String)

Long

调用java.lang.Long.valueOf(String)

Short

调用java.lang.Short.valueOf(String)

Object

New String(string-literal)

 

Duke书店示例中,bookstore2/web/bookdetails.jsp页面中展示了如何使用setProperty元素设置当前书籍为一个请求参数的方法。

<c:set var="bid" value="${param.bookId}"/>

<jsp:setProperty name="bookDB" property="bookId"  value="${bid}" />

下面的代码们于bookstore2/web/bookshowcart.jsp页面中,这段代码展示了如何初始化一个BookDB对象。因为初始化使用的是useBean元素,所以只有当对象创建时才被调用。

<jsp:useBean id="bookDB" class="database.BookDB" scope="page">

  <jsp:setProperty name="bookDB" property="database"  value="${bookDBAO}" />

</jsp:useBean>

 

获取JavaBeans组件属性值

获取JavaBeans组件属性值的主要方式是通过统一EL表达式。例如,在书店示例中获取一本书的标题使用的表达式是:

${bookDB.bookDetails.title}

另一种获取JavaBeans组件属性值的方式是使用jsp:getProperty元素。这个元素获得对象的属性并将其转换为字符串插入到输出流中。

<jsp:getProperty name="beanName" property="propName"/>

注意,beanName必须是事先使用useBean定义了一个对象的名称,而且在对象上存在相应的getPropName方法。虽然EL表达式是首选的获取对象属性的方式,但如果需要的话,随时也可以使用getProperty方式显示对象的属性。

 

使用自定义标签

自定义标签是一种自定义的JSP元素,用于封装与重用。自定义标签位于某个标签库中,而标签库中包含了一组相关的标签与实现标签所需的内容。

自定义标签的语法是:

<prefix:tag attr1="value" ... attrN="value" /> 

<prefix:tag attr1="value" ... attrN="value" >

  body

</prefix:tag>

其中,prefix用于区分不同的标签库,tag是标签的标识,而att1……attrn是用于控制标签的属性。

如果要在JSP中使用自定义标签,需要:

l  声明包含自定义标签的标签库

l  Web程序中设置使用该标签库

在第7章中详细讲述了不同类型的标签以及如何使用它们。

 

声明标签库

为了在JSP页面中声明要使用的标签库,需要使用taglib页面指令来声明要使用的标签库,该指定必须在任何使用该标签库的语句之前声明,如果忘记了声明某个标签库,那么所有使用该标签库的内容都会被编译器作为静态内容输出。

<%@ taglib prefix="tt" [tagdir=/WEB-INF/tags/dir | uri=URI ] %>

Prefix属性用于定义标签库前缀,使用前缀的目的是能够区分出在不同的标签库中定义的同名标签。

如果拥有标签库的标签文件,可以使用tagdir属性指定该文件的位置。注意,tagdir的属性值必须使用“/WEB-INF/tags/”开头。如果指定的目录未找到或者同时使用了URL属性,那么编译器将会报错。

url属性用于指定使用标签库的TLD(标签库描述符)的唯一标识。TLD是标签库的描述文档。

标签库描述符文件名必须使用“.tld”扩展名。TLD文件必须存放于WAR文件中的WEB-INF目录或其子目录中,或者放置于JAR中的某个标签库包的META-INF目录或其子目录下。可以直接或间接引用TLD

下面的指令起用引用了一个TLD文件名:

<%@ taglib prefix="tlt" uri="/WEB-INF/iterator.tld"%>

下面的指认使用了一个短逻辑名称间接引用了一个TLD

<%@ taglib prefix="tlt" uri="/tlt"%>

Iterator示例中定义和使用了一个简单的iteration标签。JSP页面中使用了逻辑名称引用TLD。在描述描述符中,使用jsp-config元素标记了程序使用的标签库,在标记中嵌套了taglib-urltaglib-location元素,分别用于标记标签库的逻辑名称与标签库的绝对位置或URL

JSTL库中使用的绝对URL如下:

l  Core(核心): http://java.sun.com/jsp/jstl/core

l  XML: http://java.sun.com/jsp/jstl/xml

l  Internationalization(国际化): http://java.sun.com/jsp/jstl/fmt

l  SQL: http://java.sun.com/jsp/jstl/sql

l  Functions: http://java.sun.com/jsp/jstl/functions

当你引用的标签库URI正好匹配标签库TLD中定义的URI时,你不是必须在web.xml中注明,JSP容器将会自动定义在JSTL中的标签库。

 

包含标签库的实现

除了声明标签库外,你还必须设置标签库对当前应用系统可用。有几种方法可以实现。标签库实现可以被打包于WAR中:标签文件位置包中的/WEB-INF/tag目录下,标签处理程序位于包中/WEB-INF/classes目录。已经打包为JAR文件的标签库/WEB-INF/lib目录下。最后,一个应用服务器可以加载一些标签库供所有应用系统使用。例如,在AS中,JSTL标签库位于<JAVAEE_HOME>/lib/目录下的appserv-jstl.jar文件中,AS将会自动加载它,所以你的应用程序无需添加它的引用。

Iterator标签库使用标签处理程序实现,实现类打包于/WEB-INF/classes目录下。

 

JSP页面中的内容复用

JSP技术中存在多种内容复用的技术。有三种直接复用技术——直接包含指令、页眉和页脚、jsp-include元素——将在这里介绍。而另一种技术是使用自定义标签在不同的Web程序之间间接复用将在第7章介绍。

包含指令将在JSP页面被编译为servlet时处理。其效果是将被包含的内容——无论是静态内容还是另一个JSP页面——直接插入到当前页面中。你可能会使用包含指令实现页面的横幅、版权信息、以及包含其它内容块。包含指令的语法如下:

<%@ include file=”filename” %>

例如,在Duke书店示例中所有的页面都可以包含了banner.jspf文件,用于实现页面的横幅,其语法如下:

<%@ include file=”banner.jspf” %>

另一种静态包含的方法是使用页眉与页脚机制。其方法在定义隐含包含一节讲述。这是Duke书店示例使用的方法。

因为你必须在每个要使用复用资源的页面中添加一个页面指令,所以这种方式有其一定的局限性。而页眉与页脚机制只能够应用在页面的页首与页尾的位置。如果希望使用更加灵活的复用内容块的方法,请参阅模板标签库。

容器在执行页面时处理Jsp:include元素,包含动作可以包含静态或动态的页面内容。包含静态与动态内容的处理有很大的不同,如果包含内容是静态的,那么内容将直接插入于当前位置。而如果包含的内容是动态的,那么请求将会发送给被包含的文件,该文件得到执行并返回执行结果插入到当前页面。语法如下:

<jsp:include page=”includePage”/>

hello1程序中使用了如下的语句:

<jsp:include page=”response.jsp”/>

 

转向到其它Web内容

JSP页面中的转向机制使用的其实是servlet相关的API。使用jsp:forward可以实现转向:

<jsp:forward page=”main.jsp”/>

注意,如果已经有内容输出到浏览器,jsp:forward将会引发IllegalStateException异常。

 

Jsp:param Element

当使用includeforward时,原始的request对象将会传递给目标页面,如果希望传递一些附加的参数,可以使用jsp:param元素追加传递的内容:

<jsp:include page="..." >

  <jsp:param name="param1" value="value1"/>

</jsp:include>

includeforward被执行时,目标页面将能够看到原始的reqeust对象,在对象上包含了原有的参数以及后追加的参数,当追加的参数与某个原有参数同名时,追加的参数值将会被添加到原因参数上,此时新值与旧值共存,并且新值优先。例如,原request对象包含参数A=foo,在追加一个参数A=bar后,request中将包含A=bar,foo。注意,后追加的值优先。

jsp:param参数的使用域是includeforward调用期间,这意味着,当include操作返回时新参数将不可用。

 

包含Applet

你可以在JSP页面中使用jsp:plugin元素包含一个applet或者JavaBeans组件。Jsp:plugin将生成相应的能够被浏览器正确理解的HTML标记,并诱发客户相关的下载与执行操作。Jsp:plugin语法如下:

<jsp:plugin

  type="bean|applet"

  code="objectCode"

  codebase="objectCodebase"

  { align="alignment" }

  { archive="archiveList" }

  { height="height" }

  { hspace="hspace" }

  { jreversion="jreversion" }

  { name="componentName" }

  { vspace="vspace" }

  { width="width" }

  { nspluginurl="url" }

  { iepluginurl="url" } >

  { <jsp:params>

    { <jsp:param name="paramName" value= paramValue" /> }+

  </jsp:params> }

  { <jsp:fallback> arbitrary_text </jsp:fallback> }

</jsp:plugin>

页面显现时Jsp:plugin标签将被替换为<object><embed>Jsp:plugin标签的属性用于控制其显现的内容,也是插件版本的需要。其中nspluginurliepluginurl属性替代了默认的URL,用于控制插件的下载位置。

Jsp:params元素指定了appletJavaBeans组件运行的参数。Jsp:fallback的内容在插件无法正确启动时显示。

当插件可以正确启动,但是appletJavaBeans无法正确运行时,插件将会弹出异常通知告诉用户错误信息。

书店示例中的/templet/prelude.jspf创建了一个显示动态时钟的横幅。

下面是页面中的代码:

<jsp:plugin

  type="applet"

  code="DigitalClock.class"

  codebase="/bookstore2"

  jreversion="1.4"

  align="center" height="25" width="300"

  nspluginurl="http://java.sun.com/j2se/1.4.2/download.html"

  iepluginurl="http://java.sun.com/j2se/1.4.2/download.html" >

  <jsp:params>

    <jsp:param name="language"

      value="${pageContext.request.locale.language}" />

    <jsp:param name="country"

      value="${pageContext.request.locale.country}" />

    <jsp:param name="bgcolor" value="FFFFFF" />

    <jsp:param name="fgcolor" value="CC0066" />

  </jsp:params>

  <jsp:fallback>

    <p>Unable to start plugin.</p>

  </jsp:fallback>

</jsp:plugin>

 

设置JSP页面组的属性

可以为一组JSP页面指定以下几个方面的属性:

l  表达式计算方面

l  涉及到脚本元素方面

l  页面编码

l  自动页眉与页脚包含

一个页面组是一个已命名的文件集合,通过指定一个或多个URL模式来匹配文件。在文件组上的设置会应用到所有符合其URL模式的文件上。如果一个文件符合多个页面组的URL模式,那么这个文件同时属于多个文件组。在NetBeans 5.5上定义文件组的方式如下:

l  打开部署描述符文件——web.xml

l  选择顶部的“页面”标签

l  选择JSP属性组,点击添加JSP属性组

l  输入页面组的名称

l  输入URL模式(一个正则表达式,如*.jsp),或点击浏览选择一个文件或文件夹

l  点击OK保存

 

禁用EL表达式计算

每个JSP页面都有一个EL表达式的计算模式默认值。这个默认值依赖于Web程序部署描述的版本。在Servlet2.3及之前的早期版本中,默认忽略所有的EL表达式。而在Servlet2.4及后期版本中,默认启动EL表达式。

你可以使用页面指令中的isELIgnored属性改变对页面的EL表达式计算模式默认值,在标签文件中也可以使用标签指定的isELIgnored属性。另外,还可以通过以下方式改变默认值:

l  NetBeans中在页面组中选择忽略表达式语言选择框

l  在部署描述符中的jsp-property-group节中添加el-igored元素

下表汇总了JSP页面的EL表达式设置:

JSP配置

页面指定isELIgnored

EL表达式计算

未指定

未指定

2.3及之前版本忽略
2.4
及后绪版本计算

False

未指定

计算

True

未指定

忽略

被页面指定覆盖

False

计算

被页面指定覆盖

True

忽略

 

下表汇总了标签文件中的表达式设置:

标签指定isELIgnored

表达式计算

未指定

计算

False

计算

True

忽略

 

声明页面编码

可以在页面组中设置该组文件使用的页面编码,方法是:

l  NetBeans中在页面组属性中输入页面使用的页面编码

l  在部署描述符中的jsp-property-group节中使用page-encode元素指定一个有效的字符编码。

当你在页面中使用页面指令指定了一个与页面组中所设定的值不同的字符编码时,将会引发一个编译错误。

 

定义隐含包含

你可以通过添加页面组的头与页脚列表项隐式指定页面的页眉与页脚。列表中的列表项是上下文相关的,必须能够正确定位到相应的元素。当页面显现时,将会依次地包含指定的页眉与页脚。当指定不止一个页眉或页脚时,将按照指定的顺序来包含。

例如,在书店示例中,使用了页眉与页脚分别包含了/template/prelude.jspf/template/coda.jspf文件。

页眉与页脚只能在页面的开始与结束包含文件,如果希望更加灵活的包含文件的方法,请参阅模板标签库的内容。

 

消除多余的空白字符

默认情况下,页面中的空白字符也将显现给浏览器,这可能会引起一些问题。例如,在某个taglib指令后面的回车符可能会使页面上的内容另起一行。

如果希望消除空白字符,可以在部署描述符中页面组中设置trim-directive-whitespacestrue

也可以使用页面指令的trimDirectiveWhitespaces属性为空值来指定,该设置会覆盖页面组中的设置。

在标签文件中,可以使用trimDirectiveWhiteSpace属性为真值来设置忽略空白字符。

 

 

 

原创粉丝点击