JavaServer Face为Java应用程序的开发提速。

JavaServer Faces(JSF)是一项使用Java技术来快速构建Web应用程序的新技术。JSF通过提供以下特性来加速开发进程:标准和可扩展的用户界面组件;极易配置的页面导航;用于输入验证的组件;自动化的Bean管理;事件处理;轻松的错误处理,以及内置的对国际化的支持。

本文介绍如何使用JSF来构建在线比萨(pizza)订购系统。

项目描述

该应用程序被称之为PizzaRia,是一个在线商店,允许用户选择比萨饼并递交选定的比萨饼。PizzaRia与其他在线商店类似,用户可以浏览菜单,向购物车中添加所选产品并进行结账。

该应用程序的用户界面由5个JSP文件组成,它们是index.jsp, details.jsp, shoppingCart.jsp, checkOut.jsp以及order.jsp 。每个用户界面的页面包括3个其它页面:header.jsp, menu.jsp和 footer.jsp 。

Figure 1

Figure 2

Figure 3

Figure 4

Figure 5

数据库

该程序的数据存储在3张表中:products(产品)、orders(定单)和OrderDetails(订单详细项目)。Products表存储产品信息,具有4列:ProductId(产品标识), Name(名称), Description(说明)和 Price(价格)。

Orders表中的每一行存储一个单独的订单,其中的信息包括联系人姓名、送货地址以及信用卡细目。Orders表有6列:OrderId(定单标识), ContactName(联系人姓名), DeliveryAddress(送货地址), CCName(信用卡所属人姓名), CCNumber(信用卡号码)和 CCExpiryDate(信用卡有效期限)。

每个订单的详细项目被存储在OrderDetails表中。OrderDetails表有4列:OrderId(定单标识), ProductId(产品标识), Quantity(数量)和 Price(价格)。Orders与OrderDetails表通过OrderID列有一对多的对应关系。请注意,OrderDetails表在用户下订单的时候就保存相关的价格信息。该价格可能与当前产品价格不同,后者存储在Products表的Price列中。

用于在一个Oracle数据库中创建所需的表的脚本文件pizzaria-oracle.sql存放在pizzaria.zip文件中。

业务对象

以下是在该应用程序中使用的业务对象:

ProductBean用于封装一个产品信息。它具有如下属性:id(标识)、name(名称)、description(说明)和price(价格)。每次details.asp页被访问的时候,JSF实现(implementation)就会自动创建一个ProductBean实例。该JSF实现调用ProductBean的无参数构造器,从数据库中获取相关的数据,并且将其填入相应的字列中。

ProductSummary。ProductSummary(产品概要)用于表示产品的概要。该类包含2个属性:id(标识)和name(名称)。

ShoppingItemBean。ShoppingItemBean用于表示购物项目。该类包含4个属性:productId(产品标识), productName(产品名称), price(价格)以及 quantity(数量)。

ShoppingCartBean。ShoppingCartBean用于表示一个存储在对话(session)对象中的购物车。该类允许用户添加购物项目(使用addShopping方法),获取包含所有购物项目的列表(使用getShoppingItems方法),获得所购货物的总价值(使用getTotal方法)。

OrderBean。OrderBean表示一个订单。该类具有如下5个属性:contactName, deliveryAddress, creditCardName, creditCardNumber以及 creditCardExpiryDate。

MenuBean。MenuBean使用getMenu方法显示可供产品的目录。该方法返回一个包含到产品细节的链接的HTML表。

DatabaseUtil。DatabaseUtil提供了以下3种方法以便访问和操作数据:

  • GetProductSummaries:该方法返回一个包含了产品表中所有产品概要的列表。一个产品概要通过ProductSummary类来表示。

  • GetProductDetails:该方法返回一个ProductBean对象,该对象封装具有特定标识符的产品细节。

  • InsertOrder:该方法向Orders表和OrderDetails表插入客户订单。

应用程序上下文监听器

应用程序上下文监听器(AppContextListener类)从web.xml文件读出用于访问数据库的初始参数,然后将其写入ServletContext对象。用到的初始参数如下:jdbcDriver, dbUrl, dbUserName和 dbPassword。在你的web.xml文件中编辑这些值,以便反应你数据库的真实值。

JSF应用程序配置

JSF允许编程人员仅仅通过应用程序配置文件就可以轻松配置应用程序。该文件如果存在的话,则它应该被命名为faces-config.xml,并且应该位于你应用程序下的WEB-INF 目录。

可以在faces-config.xmlz文件中对该应用程序的多个方面进行配置,包括bean管理、页面导航、定制UI(用户界面)组件、定制验证程序和消息资源。在 PizzaRia 应用程序中,我将该faces-config.xml用于bean管理和页面导航的配置。

JavaBean管理。对于JavaBean管理,可以使用应用程序配置文件faces-config.xml中的managed-bean元件。每个managed-bean元件都会注册一个JavaBean--JSF会将该JavaBean在特定的作用域内实例化和进行储存。managed-bean元件定义如下:

<!ELEMENT managed-bean (description*, display-name*, icon*, managed-bean
name, managed-bean-class, managed-bean-scope, (managed-property* | map-entries |
list-entries))>

每个managed-bean元件都必须包含一个managed-bean-name元件,一个managed-bean-class元件,以及一个managed-bean-scope元件,并且可选择性地包含一些描述、显示名、图标和managed-property/map-entries/list-entries元件。

managed-bean-name指定了被用来在整个应用程序中引用该JavaBean的名称。managed-bean-class元件包含该JavaBean的完全限度的类名。managed-bean-scope元件定义该JavaBean的作用域。该元件可能的值是:application、session、request或者none。如果managed-bean-scope元件是none以外的其他值,那么,所创建的该JavaBean元件将会被存储在相应的对象中。比如说,如果值是"session",那么,该JavaBean就会被存储在一个给定用户的session对象中。

在PizzaRia应用程序中,我注册了如代码清单1所示的4个JavaBeans。

页面导航:页面导航决定了Web应用程序的控制流。本节演示如何在JSF中创立一个页面导航。

JSF使用navigation-rule元件来为页面导航定义规则。其定义如下:

<!ELEMENT navigation-rule 
  (description*, display-name*, icon*, from-view-id?, navigation-case*)>

from-view-id元件是首页(起始页)的标识符。为了说明被称之为index.jsp的JSP页面的导航规则,下面给出子元件from-view-id的值:

<from-view-id>/index.jsp</from-view-id>

navigation-case元件表示一个可能的目标页面。navigation-rule一个元件可以有零个或者数个navigation-case子元件。

每个navigation-case元件都指定from-view-id的特定处理结果的目标页面。结果可以来自from-view-id元件中 UICommand组件的行动(action)属性。

navigation-case元件由如下所示的代码描述:

<!ELEMENT navigation-case 
(description*, display-name*, icon*, from-action?, from-outcome?, 
to-view-id, redirect?)>

to-view-id元件指定目标页面。from-outcome值是处理from-view-id的结果。该值来自于在from-view-id中触发了ActionEvent的 UICommand组件的行动属性。

from-action元件也表示处理from-view-id的结果。但其值来自于引发了ActionEvent的UICommand组件的行动属性的运算值。

代码清单2展示了在PizzaRia应用程序中使用的navigation-rule元件。

在JSP页面中使用UI组件

JSF提供两个定制标记库来帮助用户快速编写Web应用程序:HTML和Core。HTML定制标记库定义了用来表示UI组件的标记。Core定制标记库使用具有组件的验证器(validators)定义了注册事件处理器的核心行动,以及其他一些行动。你可以在自己的JSF应用程序的JSP页面中使用这两个库的标记。

为了在JSP页面中使用HTML和Core定制标记库,必须在页面中包含如下所示的taglib指令:

<%@ taglib uri="http://java.sun.com
/jsf/html/" prefix="h" %>
<%@ taglib uri="http://java.sun.com/
jsf/core/" prefix="f" %>

Prefix的属性值可以是任意值。但是,根据惯例,最好是使用"h"和"f"。

在JSF应用程序中编写JSP页面是每一个页面制作者的责任。除了布置组件之外,他们的责任还包括把组件绑定到模型对象数据并且把Core标记(诸如事件监听器和验证器)添加到组件标记中。

在HTML定制标记库中有25个标记。每个组件都呈现为一个HTML元件,而多个标记被呈现为同一个HTML元件。表1列出了HTML定制标记库中的标记。

标记 说明
Column 在UIData组件内表示一个数据列。
command_button 表示一个向服务器提交表单的按钮。
command_link 表示一个指向另一页面或者本页面内其他位置的超链接。
data_table 表示一个支持将数据绑定到一个数据对象的集合上的表。
Form 表示一个表单。
graphic_image 显示一张图片。
input_hidden 表示一个隐藏的元件。
input_secret 表示一个密码输入框。
input_text 表示一个可接受单个字符串的文本输入框。
input_textarea 表示一个可接受多个字符串的文本输入区。
Message 显示给定组件的信息。
Messages 表示一个从FacesContext中获取消息并且将其显示给用户的组件。
output_label 显示文本。
output_link 显示一个超链接。
output_message 显示给定组件的信息。
output_text 显示一行文本。
panel_grid 显示一张表。
panel_group 将一个组件集合分组。
selectboolean_checkbox 表示一个单选文本框。
selectmany_checkboxlist 显示一套复选框,用户从中可以选择多个值。
selectmany_listbox 表示一个多选下拉选择框,用户从中可以选择多个项目。
selectmany_menu 表示一个多选项目列表,用户从中可以选择多个项目。
selectone_listbox 表示一个单选下拉选择框,用户从中只能选择一个项目。
selectone_menu 表示单选项目列表,用户从中只能选择一个项目。
selectone_radio 表示一套单选按钮。

表1. HTML定制标记库

使用验证器

验证器使得输入确认简单化并且可以节省开发人员的大量编程时间。JSF提供一套验证器类用于确认输入到输入组件中的值。另外一种方法就是,如果现有的标准验证器不符合需要,那么开发人员还可以编写自己的验证器。

验证器是一个实现类(implementation class),它可以验证输入值,如果是非法输入,就会发出一个错误信息。可以通过将一个验证器嵌入一个其输入需要验证的输入组件中来使用它。如果该验证器判断出用户的输入是非法的,那么JSF servlet就会重新显示刚才提交了表单的那个JSP页面,而不会将本地值复制给绑定到该输入组件上的JavaBean实例。

JSF实现为通用的验证任务提供了3个标准验证器,包括检查必填的域内已填入内容、输入的内容符合长度和范围要求。表2列举了标准的验证器。

验证器类 标记 说明
LengthValidator validate_length 确保组件的本地值的长度在规定的范围之内。该值必须是字符串型。
LongRangeValidator validate_longrange 确保组件的本地值在规定的范围之内。该值必须能够被转换成长型。
DoubleRangeValidator validate_doublerange 确保组件的本地值在规定的范围内。该值必须能够被转换成浮点型。

表2.标准验证器

另外,HTML定制标记库中的input_text和input_textarea标记有必填的属性。如果将该属性标赋值为真,那么用户在继续进行操作之前,就必须对文本输入框元件或者文本输入区域进行填写。

在PizzaRia应用程序中,checkOut.jsp页面使用该必填的属性以便保证没有一个域是空的。


事件处理

JSP应用程序是事件驱动型的程序。在JSF中处理事件令人惊奇的简单。以下是处理步骤:

  1. 编写事件监听器。
  2. 在程序目录下的WEB-INF/classes or WEB-INF/lib目录中部署事件监听器。
  3. 在表示组件(其事件被捕获)的标记中,使用Core定制标记库中定义的action_listener或者 valuechange_listener标记。

在JSF中的事件对象。JSF中的所有事件对象必须提供javax.faces .event.FacesEvent类,以便这些事件被请求处理生命周期支持。FacesEvent类是java.util.EventObject的子类,并添加了getComponent方法,该方法返回引发该事件的UIComponent组件。

FacesEvent类有两个子类:ActionEvent和 ValueChangeEvent。ActionEvent类激活诸如UICommand组件之类的UI组件。

ValueChangeEvent类会发出一个通知,告知本地UIInput组件的值被修改了。然而,如果新值没有被成功地验证为合法的,则不会发出ValueChangeEvent通知。被加入到该类中的两个重要方法是getOldValue 和 getNewValue。getOldValue方法返回引发该事件的组件的旧值。getNewValue方法返回相应的新值。这两种方法的返回值类型都是java .lang.Object。
第三,JSF中的事件监听器。

为捕获一个JSF事件,需要使用一个事件监听器。JSF程序中的所有监听器都必须实现javax.faces.event.FacesListener接口。该接口提供java.util.EventListener接口,后者是必须由所有Java事件监听器实现的接口。

Faces Listener接口有两个子接口:ActionListener 和 ValueChangeListener。ActionListener接口是为了捕获ActionEvent而必须被实现的接口。该接口添加了一个新的方法--processAction--该方法请求处理生命周期来调用。当为之注册了ActionListener 的ActionEvent发生事件时,就会调用processAction。processAction方法的代码如下:

public void processAction(ActionEvent 
  event) 
 throws AbortProcessingException

ValueChangeListener接口是为了捕获ValueChangeEvent而实现的接口。该接口添加了一个方法:processValueChange。当ValueChangeEvent动作被其监听者监听到时,就会调用processValueChange方法。processValueChange方法的代码如下:

public void processValueChange(ValueChangeEvent
  event) 
throws AbortProcessingException

下一步

下载
JavaServer Faces (JSF)

PizzaRia应用程序

阅读
关于JavaServer Faces的更多信息
java.sun.com/j2ee/javaserverfaces/download.html
java.sun.com/webservices/downloads/webservicespack.html

在PizzaRia应用程序中,开发人员需要一个名为AppAction Listener 的ActionListener。其processAction方法从ActionEvent对象的getLocalValue方法获取传递给该方法的本地值。如果本地值为"Buy(购买)",则processAction获取与用户相关的ShoppingCartBean对象,并且将shoppingItem加入到bean中去。如果本地值为"pay(付款)",那么processAction就从session(会话)对象获取OrderBean对象和ShoppingCartBean对象,并调用DatabaseUtil对象的insertOrder方法。代码清单3描述了processAction方法。

AppActionListener类使用两个非常有用的方法:getValueBinding 和getDatabaseUtil。getValueBinding接受指定对象名的字符串,并返回一个可以向下转换类型为对象类型的ValueBinding对象。比如说,为获得用户的在应用程序配置文件中被注册成shoppingCartBean 的ShoppingCartBean实例,开发人员需要通过传递"shoppingCartBean"来调用getValueBinding。

ShoppingCartBean cart = (ShoppingCartBean) 
getValueBinding("#{shoppingCartBean}").getValue(facesContext);

getValueBinding方法如下:

private ValueBinding getValueBinding(String valueRef) {
  ApplicationFactory factory =
  (ApplicationFactory)FactoryFinder
.getFactory(FactoryFinder
.APPLICATION_FACTORY);
  Application application = factory.getApplication();
return
  application.createValueBinding
(valueRef);
  }

getDatabaseUtil方法返回一个对ServletContext中的DatabaseUtil实例的引用:

private DatabaseUtil getDatabaseUtil() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ServletContext servletContext = (ServletContext)
facesContext.getExternalContext()
.getContext();
return (DatabaseUtil) servletContext
.getAttribute("DATABASE_UTIL");
  }

运行该应用程序

该PizzaRia JSF应用程序用JSF参考实现(JavaServer Faces [JSF] Beta 1.0)已做过测试。请参看该应用程序的zip文件(pizzaria.zip)中的readme.txt文件,以便获得有关部署PizzaRia应用程序的更详细信息。