JavaServer Face為Java應(yīng)用程序的開發(fā)提速。

JavaServer Faces(JSF)是一項(xiàng)使用Java技術(shù)來(lái)快速構(gòu)建Web應(yīng)用程序的新技術(shù)。JSF通過(guò)提供以下特性來(lái)加速開發(fā)進(jìn)程:標(biāo)準(zhǔn)和可擴(kuò)展的用戶界面組件;極易配置的頁(yè)面導(dǎo)航;用于輸入驗(yàn)證的組件;自動(dòng)化的Bean管理;事件處理;輕松的錯(cuò)誤處理,以及內(nèi)置的對(duì)國(guó)際化的支持。

本文介紹如何使用JSF來(lái)構(gòu)建在線比薩(pizza)訂購(gòu)系統(tǒng)。

項(xiàng)目描述

該應(yīng)用程序被稱之為PizzaRia,是一個(gè)在線商店,允許用戶選擇比薩餅并遞交選定的比薩餅。PizzaRia與其他在線商店類似,用戶可以瀏覽菜單,向購(gòu)物車中添加所選產(chǎn)品并進(jìn)行結(jié)賬。

該應(yīng)用程序的用戶界面由5個(gè)JSP文件組成,它們是index.jsp, details.jsp, shoppingCart.jsp, checkOut.jsp以及order.jsp 。每個(gè)用戶界面的頁(yè)面包括3個(gè)其它頁(yè)面:header.jsp, menu.jsp和 footer.jsp 。

Figure 1

Figure 2

Figure 3

Figure 4

Figure 5

數(shù)據(jù)庫(kù)

該程序的數(shù)據(jù)存儲(chǔ)在3張表中:products(產(chǎn)品)、orders(定單)和OrderDetails(訂單詳細(xì)項(xiàng)目)。Products表存儲(chǔ)產(chǎn)品信息,具有4列:ProductId(產(chǎn)品標(biāo)識(shí)), Name(名稱), Description(說(shuō)明)和 Price(價(jià)格)。

Orders表中的每一行存儲(chǔ)一個(gè)單獨(dú)的訂單,其中的信息包括聯(lián)系人姓名、送貨地址以及信用卡細(xì)目。Orders表有6列:OrderId(定單標(biāo)識(shí)), ContactName(聯(lián)系人姓名), DeliveryAddress(送貨地址), CCName(信用卡所屬人姓名), CCNumber(信用卡號(hào)碼)和 CCExpiryDate(信用卡有效期限)。

每個(gè)訂單的詳細(xì)項(xiàng)目被存儲(chǔ)在OrderDetails表中。OrderDetails表有4列:OrderId(定單標(biāo)識(shí)), ProductId(產(chǎn)品標(biāo)識(shí)), Quantity(數(shù)量)和 Price(價(jià)格)。Orders與OrderDetails表通過(guò)OrderID列有一對(duì)多的對(duì)應(yīng)關(guān)系。請(qǐng)注意,OrderDetails表在用戶下訂單的時(shí)候就保存相關(guān)的價(jià)格信息。該價(jià)格可能與當(dāng)前產(chǎn)品價(jià)格不同,后者存儲(chǔ)在Products表的Price列中。

用于在一個(gè)Oracle數(shù)據(jù)庫(kù)中創(chuàng)建所需的表的腳本文件pizzaria-oracle.sql存放在pizzaria.zip文件中。

業(yè)務(wù)對(duì)象

以下是在該應(yīng)用程序中使用的業(yè)務(wù)對(duì)象:

ProductBean用于封裝一個(gè)產(chǎn)品信息。它具有如下屬性:id(標(biāo)識(shí))、name(名稱)、description(說(shuō)明)和price(價(jià)格)。每次details.asp頁(yè)被訪問(wèn)的時(shí)候,JSF實(shí)現(xiàn)(implementation)就會(huì)自動(dòng)創(chuàng)建一個(gè)ProductBean實(shí)例。該JSF實(shí)現(xiàn)調(diào)用ProductBean的無(wú)參數(shù)構(gòu)造器,從數(shù)據(jù)庫(kù)中獲取相關(guān)的數(shù)據(jù),并且將其填入相應(yīng)的字列中。

ProductSummary。ProductSummary(產(chǎn)品概要)用于表示產(chǎn)品的概要。該類包含2個(gè)屬性:id(標(biāo)識(shí))和name(名稱)。

ShoppingItemBean。ShoppingItemBean用于表示購(gòu)物項(xiàng)目。該類包含4個(gè)屬性:productId(產(chǎn)品標(biāo)識(shí)), productName(產(chǎn)品名稱), price(價(jià)格)以及 quantity(數(shù)量)。

ShoppingCartBean。ShoppingCartBean用于表示一個(gè)存儲(chǔ)在對(duì)話(session)對(duì)象中的購(gòu)物車。該類允許用戶添加購(gòu)物項(xiàng)目(使用addShopping方法),獲取包含所有購(gòu)物項(xiàng)目的列表(使用getShoppingItems方法),獲得所購(gòu)貨物的總價(jià)值(使用getTotal方法)。

OrderBean。OrderBean表示一個(gè)訂單。該類具有如下5個(gè)屬性:contactName, deliveryAddress, creditCardName, creditCardNumber以及 creditCardExpiryDate。

MenuBean。MenuBean使用getMenu方法顯示可供產(chǎn)品的目錄。該方法返回一個(gè)包含到產(chǎn)品細(xì)節(jié)的鏈接的HTML表。

DatabaseUtil。DatabaseUtil提供了以下3種方法以便訪問(wèn)和操作數(shù)據(jù):

  • GetProductSummaries:該方法返回一個(gè)包含了產(chǎn)品表中所有產(chǎn)品概要的列表。一個(gè)產(chǎn)品概要通過(guò)ProductSummary類來(lái)表示。

  • GetProductDetails:該方法返回一個(gè)ProductBean對(duì)象,該對(duì)象封裝具有特定標(biāo)識(shí)符的產(chǎn)品細(xì)節(jié)。

  • InsertOrder:該方法向Orders表和OrderDetails表插入客戶訂單。

應(yīng)用程序上下文監(jiān)聽器

應(yīng)用程序上下文監(jiān)聽器(AppContextListener類)從web.xml文件讀出用于訪問(wèn)數(shù)據(jù)庫(kù)的初始參數(shù),然后將其寫入ServletContext對(duì)象。用到的初始參數(shù)如下:jdbcDriver, dbUrl, dbUserName和 dbPassword。在你的web.xml文件中編輯這些值,以便反應(yīng)你數(shù)據(jù)庫(kù)的真實(shí)值。

JSF應(yīng)用程序配置

JSF允許編程人員僅僅通過(guò)應(yīng)用程序配置文件就可以輕松配置應(yīng)用程序。該文件如果存在的話,則它應(yīng)該被命名為faces-config.xml,并且應(yīng)該位于你應(yīng)用程序下的WEB-INF 目錄。

可以在faces-config.xmlz文件中對(duì)該應(yīng)用程序的多個(gè)方面進(jìn)行配置,包括bean管理、頁(yè)面導(dǎo)航、定制UI(用戶界面)組件、定制驗(yàn)證程序和消息資源。在 PizzaRia 應(yīng)用程序中,我將該faces-config.xml用于bean管理和頁(yè)面導(dǎo)航的配置。

JavaBean管理。對(duì)于JavaBean管理,可以使用應(yīng)用程序配置文件faces-config.xml中的managed-bean元件。每個(gè)managed-bean元件都會(huì)注冊(cè)一個(gè)JavaBean--JSF會(huì)將該JavaBean在特定的作用域內(nèi)實(shí)例化和進(jìn)行儲(chǔ)存。managed-bean元件定義如下:

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

每個(gè)managed-bean元件都必須包含一個(gè)managed-bean-name元件,一個(gè)managed-bean-class元件,以及一個(gè)managed-bean-scope元件,并且可選擇性地包含一些描述、顯示名、圖標(biāo)和managed-property/map-entries/list-entries元件。

managed-bean-name指定了被用來(lái)在整個(gè)應(yīng)用程序中引用該JavaBean的名稱。managed-bean-class元件包含該JavaBean的完全限度的類名。managed-bean-scope元件定義該JavaBean的作用域。該元件可能的值是:application、session、request或者none。如果managed-bean-scope元件是none以外的其他值,那么,所創(chuàng)建的該JavaBean元件將會(huì)被存儲(chǔ)在相應(yīng)的對(duì)象中。比如說(shuō),如果值是"session",那么,該JavaBean就會(huì)被存儲(chǔ)在一個(gè)給定用戶的session對(duì)象中。

在PizzaRia應(yīng)用程序中,我注冊(cè)了如代碼清單1所示的4個(gè)JavaBeans。

頁(yè)面導(dǎo)航:頁(yè)面導(dǎo)航?jīng)Q定了Web應(yīng)用程序的控制流。本節(jié)演示如何在JSF中創(chuàng)立一個(gè)頁(yè)面導(dǎo)航。

JSF使用navigation-rule元件來(lái)為頁(yè)面導(dǎo)航定義規(guī)則。其定義如下:

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

from-view-id元件是首頁(yè)(起始頁(yè))的標(biāo)識(shí)符。為了說(shuō)明被稱之為index.jsp的JSP頁(yè)面的導(dǎo)航規(guī)則,下面給出子元件from-view-id的值:

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

navigation-case元件表示一個(gè)可能的目標(biāo)頁(yè)面。navigation-rule一個(gè)元件可以有零個(gè)或者數(shù)個(gè)navigation-case子元件。

每個(gè)navigation-case元件都指定from-view-id的特定處理結(jié)果的目標(biāo)頁(yè)面。結(jié)果可以來(lái)自from-view-id元件中 UICommand組件的行動(dòng)(action)屬性。

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

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

to-view-id元件指定目標(biāo)頁(yè)面。from-outcome值是處理from-view-id的結(jié)果。該值來(lái)自于在from-view-id中觸發(fā)了ActionEvent的 UICommand組件的行動(dòng)屬性。

from-action元件也表示處理from-view-id的結(jié)果。但其值來(lái)自于引發(fā)了ActionEvent的UICommand組件的行動(dòng)屬性的運(yùn)算值。

代碼清單2展示了在PizzaRia應(yīng)用程序中使用的navigation-rule元件。

在JSP頁(yè)面中使用UI組件

JSF提供兩個(gè)定制標(biāo)記庫(kù)來(lái)幫助用戶快速編寫Web應(yīng)用程序:HTML和Core。HTML定制標(biāo)記庫(kù)定義了用來(lái)表示UI組件的標(biāo)記。Core定制標(biāo)記庫(kù)使用具有組件的驗(yàn)證器(validators)定義了注冊(cè)事件處理器的核心行動(dòng),以及其他一些行動(dòng)。你可以在自己的JSF應(yīng)用程序的JSP頁(yè)面中使用這兩個(gè)庫(kù)的標(biāo)記。

為了在JSP頁(yè)面中使用HTML和Core定制標(biāo)記庫(kù),必須在頁(yè)面中包含如下所示的taglib指令:

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

Prefix的屬性值可以是任意值。但是,根據(jù)慣例,最好是使用"h"和"f"。

在JSF應(yīng)用程序中編寫JSP頁(yè)面是每一個(gè)頁(yè)面制作者的責(zé)任。除了布置組件之外,他們的責(zé)任還包括把組件綁定到模型對(duì)象數(shù)據(jù)并且把Core標(biāo)記(諸如事件監(jiān)聽器和驗(yàn)證器)添加到組件標(biāo)記中。

在HTML定制標(biāo)記庫(kù)中有25個(gè)標(biāo)記。每個(gè)組件都呈現(xiàn)為一個(gè)HTML元件,而多個(gè)標(biāo)記被呈現(xiàn)為同一個(gè)HTML元件。表1列出了HTML定制標(biāo)記庫(kù)中的標(biāo)記。

標(biāo)記 說(shuō)明
Column 在UIData組件內(nèi)表示一個(gè)數(shù)據(jù)列。
command_button 表示一個(gè)向服務(wù)器提交表單的按鈕。
command_link 表示一個(gè)指向另一頁(yè)面或者本頁(yè)面內(nèi)其他位置的超鏈接。
data_table 表示一個(gè)支持將數(shù)據(jù)綁定到一個(gè)數(shù)據(jù)對(duì)象的集合上的表。
Form 表示一個(gè)表單。
graphic_image 顯示一張圖片。
input_hidden 表示一個(gè)隱藏的元件。
input_secret 表示一個(gè)密碼輸入框。
input_text 表示一個(gè)可接受單個(gè)字符串的文本輸入框。
input_textarea 表示一個(gè)可接受多個(gè)字符串的文本輸入?yún)^(qū)。
Message 顯示給定組件的信息。
Messages 表示一個(gè)從FacesContext中獲取消息并且將其顯示給用戶的組件。
output_label 顯示文本。
output_link 顯示一個(gè)超鏈接。
output_message 顯示給定組件的信息。
output_text 顯示一行文本。
panel_grid 顯示一張表。
panel_group 將一個(gè)組件集合分組。
selectboolean_checkbox 表示一個(gè)單選文本框。
selectmany_checkboxlist 顯示一套復(fù)選框,用戶從中可以選擇多個(gè)值。
selectmany_listbox 表示一個(gè)多選下拉選擇框,用戶從中可以選擇多個(gè)項(xiàng)目。
selectmany_menu 表示一個(gè)多選項(xiàng)目列表,用戶從中可以選擇多個(gè)項(xiàng)目。
selectone_listbox 表示一個(gè)單選下拉選擇框,用戶從中只能選擇一個(gè)項(xiàng)目。
selectone_menu 表示單選項(xiàng)目列表,用戶從中只能選擇一個(gè)項(xiàng)目。
selectone_radio 表示一套單選按鈕。

表1. HTML定制標(biāo)記庫(kù)

使用驗(yàn)證器

驗(yàn)證器使得輸入確認(rèn)簡(jiǎn)單化并且可以節(jié)省開發(fā)人員的大量編程時(shí)間。JSF提供一套驗(yàn)證器類用于確認(rèn)輸入到輸入組件中的值。另外一種方法就是,如果現(xiàn)有的標(biāo)準(zhǔn)驗(yàn)證器不符合需要,那么開發(fā)人員還可以編寫自己的驗(yàn)證器。

驗(yàn)證器是一個(gè)實(shí)現(xiàn)類(implementation class),它可以驗(yàn)證輸入值,如果是非法輸入,就會(huì)發(fā)出一個(gè)錯(cuò)誤信息。可以通過(guò)將一個(gè)驗(yàn)證器嵌入一個(gè)其輸入需要驗(yàn)證的輸入組件中來(lái)使用它。如果該驗(yàn)證器判斷出用戶的輸入是非法的,那么JSF servlet就會(huì)重新顯示剛才提交了表單的那個(gè)JSP頁(yè)面,而不會(huì)將本地值復(fù)制給綁定到該輸入組件上的JavaBean實(shí)例。

JSF實(shí)現(xiàn)為通用的驗(yàn)證任務(wù)提供了3個(gè)標(biāo)準(zhǔn)驗(yàn)證器,包括檢查必填的域內(nèi)已填入內(nèi)容、輸入的內(nèi)容符合長(zhǎng)度和范圍要求。表2列舉了標(biāo)準(zhǔn)的驗(yàn)證器。

驗(yàn)證器類 標(biāo)記 說(shuō)明
LengthValidator validate_length 確保組件的本地值的長(zhǎng)度在規(guī)定的范圍之內(nèi)。該值必須是字符串型。
LongRangeValidator validate_longrange 確保組件的本地值在規(guī)定的范圍之內(nèi)。該值必須能夠被轉(zhuǎn)換成長(zhǎng)型。
DoubleRangeValidator validate_doublerange 確保組件的本地值在規(guī)定的范圍內(nèi)。該值必須能夠被轉(zhuǎn)換成浮點(diǎn)型。

表2.標(biāo)準(zhǔn)驗(yàn)證器

另外,HTML定制標(biāo)記庫(kù)中的input_text和input_textarea標(biāo)記有必填的屬性。如果將該屬性標(biāo)賦值為真,那么用戶在繼續(xù)進(jìn)行操作之前,就必須對(duì)文本輸入框元件或者文本輸入?yún)^(qū)域進(jìn)行填寫。

在PizzaRia應(yīng)用程序中,checkOut.jsp頁(yè)面使用該必填的屬性以便保證沒(méi)有一個(gè)域是空的。


事件處理

JSP應(yīng)用程序是事件驅(qū)動(dòng)型的程序。在JSF中處理事件令人驚奇的簡(jiǎn)單。以下是處理步驟:

  1. 編寫事件監(jiān)聽器。
  2. 在程序目錄下的WEB-INF/classes or WEB-INF/lib目錄中部署事件監(jiān)聽器。
  3. 在表示組件(其事件被捕獲)的標(biāo)記中,使用Core定制標(biāo)記庫(kù)中定義的action_listener或者 valuechange_listener標(biāo)記。

在JSF中的事件對(duì)象。JSF中的所有事件對(duì)象必須提供javax.faces .event.FacesEvent類,以便這些事件被請(qǐng)求處理生命周期支持。FacesEvent類是java.util.EventObject的子類,并添加了getComponent方法,該方法返回引發(fā)該事件的UIComponent組件。

FacesEvent類有兩個(gè)子類:ActionEvent和 ValueChangeEvent。ActionEvent類激活諸如UICommand組件之類的UI組件。

ValueChangeEvent類會(huì)發(fā)出一個(gè)通知,告知本地UIInput組件的值被修改了。然而,如果新值沒(méi)有被成功地驗(yàn)證為合法的,則不會(huì)發(fā)出ValueChangeEvent通知。被加入到該類中的兩個(gè)重要方法是getOldValue 和 getNewValue。getOldValue方法返回引發(fā)該事件的組件的舊值。getNewValue方法返回相應(yīng)的新值。這兩種方法的返回值類型都是java .lang.Object。
第三,JSF中的事件監(jiān)聽器。

為捕獲一個(gè)JSF事件,需要使用一個(gè)事件監(jiān)聽器。JSF程序中的所有監(jiān)聽器都必須實(shí)現(xiàn)javax.faces.event.FacesListener接口。該接口提供java.util.EventListener接口,后者是必須由所有Java事件監(jiān)聽器實(shí)現(xiàn)的接口。

Faces Listener接口有兩個(gè)子接口:ActionListener 和 ValueChangeListener。ActionListener接口是為了捕獲ActionEvent而必須被實(shí)現(xiàn)的接口。該接口添加了一個(gè)新的方法--processAction--該方法請(qǐng)求處理生命周期來(lái)調(diào)用。當(dāng)為之注冊(cè)了ActionListener 的ActionEvent發(fā)生事件時(shí),就會(huì)調(diào)用processAction。processAction方法的代碼如下:

public void processAction(ActionEvent 
  event) 
 throws AbortProcessingException

ValueChangeListener接口是為了捕獲ValueChangeEvent而實(shí)現(xiàn)的接口。該接口添加了一個(gè)方法:processValueChange。當(dāng)ValueChangeEvent動(dòng)作被其監(jiān)聽者監(jiān)聽到時(shí),就會(huì)調(diào)用processValueChange方法。processValueChange方法的代碼如下:

public void processValueChange(ValueChangeEvent
  event) 
throws AbortProcessingException

下一步

下載
JavaServer Faces (JSF)

PizzaRia應(yīng)用程序

閱讀
關(guān)于JavaServer Faces的更多信息
java.sun.com/j2ee/javaserverfaces/download.html
java.sun.com/webservices/downloads/webservicespack.html

在PizzaRia應(yīng)用程序中,開發(fā)人員需要一個(gè)名為AppAction Listener 的ActionListener。其processAction方法從ActionEvent對(duì)象的getLocalValue方法獲取傳遞給該方法的本地值。如果本地值為"Buy(購(gòu)買)",則processAction獲取與用戶相關(guān)的ShoppingCartBean對(duì)象,并且將shoppingItem加入到bean中去。如果本地值為"pay(付款)",那么processAction就從session(會(huì)話)對(duì)象獲取OrderBean對(duì)象和ShoppingCartBean對(duì)象,并調(diào)用DatabaseUtil對(duì)象的insertOrder方法。代碼清單3描述了processAction方法。

AppActionListener類使用兩個(gè)非常有用的方法:getValueBinding 和getDatabaseUtil。getValueBinding接受指定對(duì)象名的字符串,并返回一個(gè)可以向下轉(zhuǎn)換類型為對(duì)象類型的ValueBinding對(duì)象。比如說(shuō),為獲得用戶的在應(yīng)用程序配置文件中被注冊(cè)成shoppingCartBean 的ShoppingCartBean實(shí)例,開發(fā)人員需要通過(guò)傳遞"shoppingCartBean"來(lái)調(diào)用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方法返回一個(gè)對(duì)ServletContext中的DatabaseUtil實(shí)例的引用:

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

運(yùn)行該應(yīng)用程序

該P(yáng)izzaRia JSF應(yīng)用程序用JSF參考實(shí)現(xiàn)(JavaServer Faces [JSF] Beta 1.0)已做過(guò)測(cè)試。請(qǐng)參看該應(yīng)用程序的zip文件(pizzaria.zip)中的readme.txt文件,以便獲得有關(guān)部署PizzaRia應(yīng)用程序的更詳細(xì)信息。