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 。
![]() |
![]() |
![]() |
![]() |
![]() |
數(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 | 表示一套單選按鈕。 |
使用驗(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)型。 |
另外,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)單。以下是處理步驟:
- 編寫事件監(jiān)聽器。
- 在程序目錄下的WEB-INF/classes or WEB-INF/lib目錄中部署事件監(jiān)聽器。
- 在表示組件(其事件被捕獲)的標(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
閱讀 |
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ì)信息。