JSF for Nonbelievers: Clearing the FUD about JSF -- Richard Hightower
來(lái)自:http://www-128.ibm.com/developerworks/library/j-jsf1/
翻譯:zhouy
--------------------------------------------------------------------------------
恐懼、不確定和懷疑
JSF
的觀念環(huán)繞著
JSF
技術(shù)已經(jīng)有一段時(shí)間了,我覺(jué)得是阻止這種觀念繼續(xù)曼延的時(shí)候了——或者至少在兩者這間取得平衡點(diǎn)。關(guān)于
JSF
,首當(dāng)其充的誤解,就是離不開(kāi)拖拽式所見(jiàn)即所得工具進(jìn)行
JSF
開(kāi)發(fā)。其二,
JSF
不支持類(lèi)似
Struts
的
MVC Model 2
框架。而最后一點(diǎn),也是困擾
JSF
開(kāi)發(fā)最大的一點(diǎn),就是關(guān)于其開(kāi)發(fā)困難度的說(shuō)法。
在這個(gè)四小節(jié)的討論中,我將盡我所能,通過(guò)教你如何利用JSF進(jìn)行開(kāi)發(fā)這種最實(shí)際的方法,來(lái)消除上述所有三種誤解。事實(shí)上,如果你覺(jué)得JSF開(kāi)發(fā)是件困難的事,那么很有可能是因?yàn)槟銢](méi)有正確的使用它——幸運(yùn)的是要修正它并不難。我會(huì)讓你對(duì)JSF有一個(gè)大體的觀念,并且利用一個(gè)可工作的示例展示MVC和JSF的基本原理。在我們開(kāi)始所有介紹之前,先花一分鐘時(shí)間將事實(shí)不符的FUD區(qū)分清楚。
不要相信FUD
如前面提到的,關(guān)于JSF有三個(gè)大的誤解,其一便是JSF要求所見(jiàn)即所得工具進(jìn)行開(kāi)發(fā)工作。這是錯(cuò)誤的,就像許多Swing開(kāi)發(fā)人員不使用所見(jiàn)即所得工具構(gòu)建Swing應(yīng)用程序一樣,你不必需要所見(jiàn)即所得編輯器來(lái)構(gòu)建JSF應(yīng)用。事實(shí)上,不使用所見(jiàn)即所得工具來(lái)開(kāi)發(fā) JSF 會(huì)比傳統(tǒng)的Model 2框架如Struts和WebWork開(kāi)發(fā)來(lái)得簡(jiǎn)單。在稍后的章節(jié)中我會(huì)有詳細(xì)的解釋?zhuān)谶@里請(qǐng)記住:JSF開(kāi)發(fā)比Struts來(lái)得簡(jiǎn)單,即使不使用所見(jiàn)即所得工具!
關(guān)于JSF的另一個(gè)誤解是,它不支持Model 2模型。現(xiàn)在,這種說(shuō)法只是部分正確。事實(shí)是Model 2是一種建立在Servlets基礎(chǔ)上的MVC瀑布模型版本。與Model 2面向無(wú)狀態(tài)協(xié)議(HTTP)所不同的是,JSF支持富MVC模型——一種與傳統(tǒng)的GUI應(yīng)用更加相似的模型。雖然MVC的基礎(chǔ)概念使得JSF框架的實(shí)現(xiàn)比其它框架困難,但在上層結(jié)構(gòu)上許多實(shí)現(xiàn)JSF的工作已經(jīng)為你做好,得到的結(jié)果是,付出減少,卻能夠獲得更多的回報(bào)。
流傳最廣的關(guān)于JSF的誤解是在于JSF的開(kāi)發(fā)難度。我經(jīng)常從人們口中聽(tīng)到這樣的說(shuō)法,他們閱讀了大量的技術(shù)文檔,但是并沒(méi)有實(shí)際動(dòng)手去嘗試這項(xiàng)技術(shù),所以我認(rèn)為要消除這個(gè)慮慮很簡(jiǎn)單。如果你是通過(guò) JSF 浩瀚無(wú)邊的規(guī)格說(shuō)明來(lái)建立你對(duì)JSF的理解,那么這些東西肯定會(huì)使你受驚嚇。要明確的是,所有的這些規(guī)格本質(zhì)上是給實(shí)現(xiàn)工具使用的,而不是應(yīng)用程序開(kāi)發(fā)者。如前所述,JSF是為方便應(yīng)用程序開(kāi)發(fā)者而設(shè)計(jì)的。
雖然從某種意義上說(shuō),JSF的組件基礎(chǔ)和事件驅(qū)動(dòng)的GUI式的開(kāi)發(fā)模型對(duì)于Java世界來(lái)說(shuō)是一種新鮮的事物,但是它其實(shí)早已在某些領(lǐng)域中存在了。Apple的WebObjects和ASP.net與JSF就十分的相似。Tapestry是一項(xiàng)開(kāi)源的Web組件框架,它與JSF所采用的方法不同,但也是一種基于Web GUI組件框型的技術(shù)。
無(wú)論如何,關(guān)于FUD的討論或許可以終止。解決關(guān)于JSF的編見(jiàn)最快捷的方法便是正確地研究這門(mén)技術(shù),這正是我們稍后要做的事情。考慮到這或許是你第一次了解JSF,我將為你介紹它的總體概念。
給JSF初學(xué)者
類(lèi)似于Swing和AWT,JSF提供了一套標(biāo)準(zhǔn)和可重用的GUI組件。JSF被用來(lái)構(gòu)建Web應(yīng)用表層,它提供如下開(kāi)發(fā)優(yōu)勢(shì):
-
動(dòng)作邏輯與表現(xiàn)邏輯的明顯區(qū)分
-
有狀態(tài)事務(wù)的組件級(jí)別控制
-
事件與服務(wù)端代碼的輕松綁定
-
UI組件及Web層觀念
-
提供多樣的、標(biāo)準(zhǔn)的開(kāi)發(fā)商實(shí)現(xiàn)規(guī)則
一個(gè)典型的JSF應(yīng)用包括以下幾個(gè)部分:
-
供管理應(yīng)用狀態(tài)和動(dòng)作的JavaBeans組件
-
事件驅(qū)動(dòng)開(kāi)發(fā)(通過(guò)與傳統(tǒng)GUI開(kāi)發(fā)類(lèi)似的監(jiān)聽(tīng)器來(lái)實(shí)現(xiàn))
-
展示MVC樣式的頁(yè)面,通過(guò)JSF組件樹(shù)引用視圖
雖然你將需要克服使用JSF過(guò)程中的觀念上的困難,但是這些努力都將是值得的。JSF的組件狀態(tài)管理,方便的用戶(hù)輸入校驗(yàn),小粒度,組件基礎(chǔ)的事件處理及方便的可擴(kuò)展框架將使你的Web開(kāi)發(fā)便得簡(jiǎn)單。我將在接下來(lái)的幾章中用最詳細(xì)的解釋說(shuō)明這些最重要的特征。
組件基礎(chǔ)的框架模式
JSF為標(biāo)準(zhǔn)HTML中每個(gè)可用的輸入域提供了組件標(biāo)簽,你也可以為你應(yīng)用中的特殊目的定義自己的組件,或者也可以將多項(xiàng)HTML組件組合起來(lái)成為一個(gè)新的組合——例如一個(gè)數(shù)據(jù)采集組件可以包含三個(gè)下拉菜單。JSF組件是有狀態(tài)的,組件的狀態(tài)由JSF框架來(lái)提供,JSF用組件來(lái)處理HTML響應(yīng)。
JSF的組件集包含事件發(fā)布模型、一個(gè)輕量級(jí)的IoC容量、擁有其它普遍的GUI特征的組件,包括可插入的渲染、服務(wù)器端校驗(yàn)、數(shù)據(jù)轉(zhuǎn)換、頁(yè)面導(dǎo)航控制等等。作為一種基于組件的框架,JSF極具可配置性和可護(hù)展性。大部分的JSF功能,比如導(dǎo)航和受管bean查詢(xún),可以被可插入組件所替換。這樣的插入程度給開(kāi)發(fā)人員構(gòu)建Web應(yīng)用GUI時(shí)提供了極大的彈性,允許開(kāi)發(fā)人員將其它基于組件的技術(shù)應(yīng)用于JSF的開(kāi)發(fā)過(guò)程中來(lái)。比如說(shuō),你可以將JSF的內(nèi)嵌IoC框架替換為更加全能的Spring的IoC/AOP以供受管bean查詢(xún)。
JSF和JSP技術(shù)
一個(gè)JSF應(yīng)用的用戶(hù)表層被包含在JSP(JavaServer Pages)頁(yè)面中。每個(gè)JSP頁(yè)面包含有JSF組件,這些組件描繪了GUI功能。你可以在JSP頁(yè)面里使用 JSF 定制標(biāo)簽庫(kù)來(lái)渲染UI組件,注冊(cè)事件句柄,實(shí)現(xiàn)組件與校驗(yàn)器的交互及組件與數(shù)據(jù)轉(zhuǎn)換器的交互等等。
那就是說(shuō),JSF不是固定與JSP技術(shù)相關(guān)聯(lián)。事實(shí)上,JSF標(biāo)簽僅僅引用組件以實(shí)現(xiàn)顯示的目的。你會(huì)發(fā)現(xiàn)當(dāng)你第一次修改一個(gè)JSP頁(yè)面中某一JSF組件的屬性,在重新載入頁(yè)面的時(shí)候并沒(méi)有發(fā)生應(yīng)有的變化。這是因?yàn)闃?biāo)簽在它自己的當(dāng)前狀態(tài)中進(jìn)行查詢(xún),如果組件已經(jīng)存在,標(biāo)簽就不會(huì)改變它的狀態(tài)。組件模型允許控制代碼改變一個(gè)組件的狀態(tài)(例如將一個(gè)輸入域置為不可用),當(dāng)視圖被展現(xiàn)的時(shí)候,組件樹(shù)的當(dāng)前狀態(tài)也隨著一覽無(wú)余。
一個(gè)典型的JSF應(yīng)用在UI層無(wú)需任何的Java代碼,只需要很少的一部份JSTL EL( JSP 標(biāo)準(zhǔn)標(biāo)簽庫(kù),表達(dá)式語(yǔ)言)。前面已經(jīng)提及,有非常多IDE工具可以幫助我們構(gòu)建JSF應(yīng)用,并且有越來(lái)越多的第三方JSF GUI組件市場(chǎng)。不使用所見(jiàn)即所得工具來(lái)編寫(xiě)JSF也是可行的。
JSF和MVC
JSF技術(shù)是在多年Java平臺(tái)上的Web開(kāi)發(fā)技術(shù)的總結(jié)的產(chǎn)物。這種趨勢(shì)起源于JSP。JSP是一種很好的表現(xiàn)層,但同時(shí)它容易將Java代碼與HTML頁(yè)面混淆起來(lái)。另一個(gè)不好的原因與Model 1架構(gòu)有關(guān),它使得開(kāi)發(fā)人員通過(guò)使用 <jsp:useBean> 標(biāo)簽將許多原本應(yīng)該在后端處理的代碼引入到Web頁(yè)面中來(lái)。這種方法在簡(jiǎn)單的Web應(yīng)用中可以運(yùn)行得很好,但許多Java開(kāi)發(fā)者不喜歡這種類(lèi)似C++的靜態(tài)引入組合方式,所以Model 2架構(gòu)隨之被引進(jìn)。
本質(zhì)上,Model 2架構(gòu)是MVC Web應(yīng)用的一種瀑布模型版本。在Model 2架構(gòu)中控制器通過(guò)Servlets來(lái)表現(xiàn),顯示層則通過(guò)JSP頁(yè)面來(lái)展現(xiàn)。Struts是一種最簡(jiǎn)單的Model 2實(shí)現(xiàn),它使用Actions取代了Servlets。在Struts中應(yīng)用的控制邏輯與數(shù)據(jù)層(通過(guò)ActionForms來(lái)展現(xiàn))相分離。反對(duì)Struts的主要的不滿(mǎn)在于,Struts更多偏向程序化,而非面向?qū)ο蟆ebWork和Spring MVC是Model 2的另外兩種框架,它們比起Struts更大的改進(jìn)在于盡量減少程序化,但是兩者都沒(méi)有Struts普及。另外兩者也不像JSF般提供組件模型。
大多數(shù)Model 2框架真正的因素在于它們的事件模型顯得過(guò)于單薄,留下了太多的工作需要開(kāi)發(fā)人員自己來(lái)處理。一個(gè)富事件模型使多數(shù)用戶(hù)所希望的交互變得簡(jiǎn)單。許多Model 2技術(shù)就像JPS一樣,很容易將HTML布局及格式與GUI標(biāo)簽相混合,在表現(xiàn)上不像真正的組件。而一些Model 2的架構(gòu)(如Struts)錯(cuò)誤地將表現(xiàn)與狀態(tài)分離,便得許多Java開(kāi)發(fā)人員感覺(jué)他們像是在編寫(xiě)COBOL程序。
富MVC環(huán)境
JSF提供一種組件模型及比其它Model 2實(shí)現(xiàn)更為豐富的MVC環(huán)境。本質(zhì)上說(shuō),JSF比Model 2架構(gòu)更接近真正的MVC開(kāi)發(fā)環(huán)境,雖然它仍然屬于無(wú)狀態(tài)協(xié)議。JSF能夠構(gòu)建比起其它Model 2框架更精彩的事件驅(qū)動(dòng) GUI 。JSF提供一系列事件選項(xiàng)如menu選項(xiàng)的selected事件,button的clicked事件等等,這與在其它Model 2框架中更多地依賴(lài)簡(jiǎn)單的“request received”不同。
JSF的易于翻轉(zhuǎn)的事件模型允許應(yīng)用程序更少地依賴(lài)HTTP實(shí)現(xiàn)細(xì)節(jié),簡(jiǎn)化你的開(kāi)發(fā)量。JSF改善了傳統(tǒng)Model 2架構(gòu),它更容易將表現(xiàn)層和業(yè)務(wù)層移出控制器,并將業(yè)務(wù)邏輯從JSP頁(yè)面中移出。事實(shí)上,簡(jiǎn)單的控制器類(lèi)根本無(wú)需與JSF相關(guān)聯(lián),方便了對(duì)控制器類(lèi)的測(cè)試。與真正的MVC架構(gòu)不同,JSF不會(huì)在多于一個(gè)視點(diǎn)上發(fā)出多條事件,我們?nèi)匀皇窃谔幚硪粋€(gè)無(wú)狀態(tài)的協(xié)議,這使得這一點(diǎn)變得無(wú)關(guān)緊要。系統(tǒng)事件對(duì)一個(gè)視圖的變動(dòng)或更新通常來(lái)自于用戶(hù)的請(qǐng)求。
JSF的MVC實(shí)現(xiàn)細(xì)節(jié)
在JSF的MVC實(shí)現(xiàn)中,作為映射的backing bean在視圖和模型間扮演著協(xié)調(diào)的作用。正因如此,在backing bean里限制業(yè)務(wù)邏輯和持久層邏輯就顯得猶為重要。一種普遍的做法是將業(yè)務(wù)邏輯置入應(yīng)用模型中。這樣的話(huà)backing bean仍會(huì)映射模型對(duì)象以供視圖顯示。另一種選擇是將業(yè)務(wù)邏輯放入業(yè)務(wù)代理——一種與模型相作用的表層。
與JSP技術(shù)不同,JSF的視圖實(shí)現(xiàn)是一種有狀態(tài)組件模型。JSF的視圖由兩部分組成:根視圖和JSP頁(yè)面。根視圖是UI組件的集合,它負(fù)責(zé)維護(hù)UI的狀態(tài)。就如像Swing和AWT,JSF組件使用組合式設(shè)計(jì)模式來(lái)管理組件樹(shù),用按鈕來(lái)管理事件句柄及動(dòng)作方法。
Figure 1 通過(guò)MVC角度來(lái)透析的示例
?
一個(gè)
JSF
例子
在文章剩余的章節(jié)里,我將專(zhuān)注于構(gòu)建一個(gè)
JSF
應(yīng)用的過(guò)程。這個(gè)例子是
JSF
技術(shù)的一個(gè)非常簡(jiǎn)單的展現(xiàn),主要表現(xiàn)在以下幾個(gè)方面:
-
如何設(shè)置
JSF
應(yīng)用程序的格局
-
如何為
JSF
配置
web.xml
文件
-
如何為程序配置
faces-config.xml
文件
-
編寫(xiě)模型(即
backing bean
)
-
利用
JSP
技術(shù)構(gòu)建視圖
-
利用傳統(tǒng)標(biāo)簽庫(kù)在根視圖框建組件樹(shù)
-
Form
的默認(rèn)校驗(yàn)規(guī)則
例子是一個(gè)簡(jiǎn)單的計(jì)算器程序,使目標(biāo)用戶(hù)能夠輸入兩個(gè)數(shù)并計(jì)算。因此頁(yè)面上有兩個(gè)文本域,兩個(gè)標(biāo)簽,兩處錯(cuò)誤提示和一個(gè)提交按鈕。文本域用來(lái)輸入數(shù)字,標(biāo)簽用來(lái)標(biāo)示輸入的文本域,錯(cuò)誤提示用來(lái)顯示文本域中的輸入在校驗(yàn)或數(shù)據(jù)轉(zhuǎn)換時(shí)出現(xiàn)的錯(cuò)誤。總共有三個(gè)
JSP
頁(yè)面:
index.jsp
用來(lái)重定向到
calculator.jsp
頁(yè)面;
calculator.jsp
用來(lái)顯示上面提及的
GUI
;
result.jsp
用來(lái)顯示最后結(jié)果。一個(gè)受管
bean
:
CalculatorController
作為
calculator.jsp
和
result.jsp
的
backing bean
。
?
Figure
2
示例程序的第二張
MVC
視圖
構(gòu)建應(yīng)用
?
為了構(gòu)建計(jì)算器程序,你需要如下步驟:
-
?
收集
web.xml
和
faces-config.xml
文件,可以在下面的示例源代碼中找到。
-
在
web.xml
中聲明
Faces Servlet
和
Faces Servlet
映射。
-
在
web.xml
中指定
faces-config.xml
。
-
在
faces-config.xml
中聲明受管于
JSF
的
bean
。
-
在
faces-config.xml
中聲明導(dǎo)航規(guī)則。
-
構(gòu)建模型對(duì)象
Calculator
。
-
用
CalculatorController
與
Calculator
交互。
-
創(chuàng)建
index.jsp
頁(yè)面。
?
-
創(chuàng)建
calculator.jsp
頁(yè)面。
-
創(chuàng)建
results.jsp
頁(yè)面。
聲明
Faces Servlet
和
Servlet
映射
?
為了使用
Faces
,你需要在
web.xml
中裝載
Faces Servlet
如下:
<!-- Faces Servlet -->
<servlet>
? <servlet-name>Faces Servlet</servlet-name>
? <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
? <load-on-startup>1</load-on-startup>
</servlet>
這和大多數(shù)web.xml描述文件類(lèi)似,所不同的是將request的掌握權(quán)轉(zhuǎn)向JSF Servlet,而非自定義的Servlet。所有請(qǐng)求引用了f:view的JSP文件的request都將通過(guò)此Servlet。因此需要增加相應(yīng)的映射,并且將允許使用JSF的JSP技術(shù)通過(guò)映射裝載進(jìn)來(lái)。
<!-- Faces Servlet Mapping -->
<servlet-mapping>
??? <servlet-name>Faces Servlet</servlet-name>
??? <url-pattern>/calc/*</url-pattern>
</servlet-mapping>
以上配置告知Faces
Servlet容器將所有符合/calc/的請(qǐng)求都轉(zhuǎn)給Faces Servlet處理。這使得JSF能夠初始化JSF內(nèi)容和根視圖。
定義faces-config.xml文件
如果你將你的faces配置文件命名為faces-config.xml并將其放置在WEB-INF目錄下,Faces Servlet會(huì)查找到它并自動(dòng)使用它。或者你也可以通過(guò)web.xml?中的一個(gè)初始化參數(shù)裝載一個(gè)或多個(gè)應(yīng)用配置文件——javax.faces.application.CONFIG_FILES——通過(guò)逗號(hào)將作為參數(shù)的文件列表分隔開(kāi)來(lái)。或許你會(huì)使用第二種方法為所有的JSF應(yīng)用程序作配置。
聲明bean管理
接著是聲明哪個(gè)bean被JSF GUI組件所使用。在示例中只有一個(gè)受管bean,在faces-config.xml中配置如下:
<faces-config>
?? ?...
? <managed-bean>
??? <description>
????? The "backing file" bean that backs up the calculator webapp
??? </description>
??? <managed-bean-name>CalcBean</managed-bean-name>
<managed-bean-class>
?? ?com.arcmind.jsfquickstart.controller.CalculatorController
</managed-bean-class>
??? <managed-bean-scope>session</managed-bean-scope>
? </managed-bean>
</faces-config>
上面的配置告知JSF要往JSF環(huán)境中增加一個(gè)叫做CalcBean的bean,你也可以把受管bean取任何名字。Bean已經(jīng)聲明了,然后你要做的就是為應(yīng)用定義導(dǎo)航規(guī)則。
定義導(dǎo)航規(guī)則
這樣一個(gè)簡(jiǎn)單的應(yīng)用,你只需使導(dǎo)航規(guī)則從calculator.jsp到result.jsp頁(yè)面即可。如下:
<navigation-rule>
? <from-view-id>/calculator.jsp</from-view-id>
? <navigation-case>
??? <from-outcome>success</from-outcome>
??? <to-view-id>/results.jsp</to-view-id>
? </navigation-case>
</navigation-rule>
上面的代碼表明,如果一個(gè)動(dòng)作從/calculator.jsp頁(yè)面返回邏輯輸出為“success”,那么它將用戶(hù)轉(zhuǎn)向/result.jsp頁(yè)面。
?視計(jì)模型對(duì)象
?由于我們的目標(biāo)是介紹如何開(kāi)始JSF,所以我將模型對(duì)象設(shè)計(jì)得非常簡(jiǎn)單。這個(gè)模型包含在一個(gè)對(duì)象里,如Listing
1所示。
?Listing
1. The Calculator app's model object
package com.arcmind.jsfquickstart.model;
/**
?* Calculator
?*
?* @author Rick Hightower
?* @version 0.1
?*/
public class Calculator {
??? //~Methods------------------------------------------------
??? /**
???? * add numbers.
???? *
???? * @param a first number
???? * @param b second number
???? *
???? * @return result
???? */
??? public int add(int a, int b) {
??????? return a + b;
??? }
??? /**
???? * multiply numbers.
???? *
???? * @param a first number
???? * @param b second number
???? *
???? * @return result
???? */
??? public int multiply(int a, int b) {
??????? return a * b;
??? }
}
這樣,業(yè)務(wù)邏輯就全部建立了。下一步便是將其顯示在Web表現(xiàn)層上。?結(jié)合模型和視圖
?控制器的目的就是將模型與視圖結(jié)合在一起。控制器對(duì)象的一個(gè)作用就是保持模型與視圖的不可知論。(?)如同你在下面所看到的,控制器定義了三個(gè)JavaBeans屬性,通過(guò)這些屬些控制輸入和輸出結(jié)果。這些屬性是:results(作為輸出)、firstNumber(作為輸入)、secondNumber(作為輸入)。Listing 2是CalculatorController的代碼。
?Listing 2. The
CalculatorController
package com.arcmind.jsfquickstart.controller;
import com.arcmind.jsfquickstart.model.Calculator;
/**
?* Calculator Controller
?*
?* @author $author$
?* @version $Revision$
?*/
public class CalculatorController {
??? //~ Instance fields --------------------------------------------------------
??? /**
???? * Represent the model object.
???? */
??? private Calculator calculator = new Calculator();
??? /** First number used in operation. */
??? private int firstNumber = 0;
??? /** Result of operation on first number and second number. */
??? private int result = 0;
??? /** Second number used in operation. */
??? private int secondNumber = 0;
??? //~ Constructors -----------------------------------------------------------
??? /**
???? * Creates a new CalculatorController object.
???? */
??? public CalculatorController() {
??????? super();
??? }
??? //~ Methods ----------------------------------------------------------------
??? /**
???? * Calculator, this class represent the model.
???? *
???? * @param aCalculator The calculator to set.
???? */
??? public void setCalculator(Calculator aCalculator) {
??????? this.calculator = aCalculator;
??? }
??? /**
???? * First Number property
???? *
???? * @param aFirstNumber first number
???? */
??? public void setFirstNumber(int aFirstNumber) {
??????? this.firstNumber = aFirstNumber;
??? }
??? /**
???? * First number property
???? *
???? * @return First number.
???? */
??? public int getFirstNumber() {
??????? return firstNumber;
??? }
??? /**
???? * Result of the operation on the first two numbers.
???? *
???? * @return Second Number.
???? */
??? public int getResult() {
??????? return result;
??? }
??? /**
???? * Second number property
???? *
???? * @param aSecondNumber Second number.
???? */
??? public void setSecondNumber(int aSecondNumber) {
??????? this.secondNumber = aSecondNumber;
??? }
??? /**
???? * Get second number.
???? *
???? * @return Second number.
???? */
??? public int getSecondNumber() {
??????? return secondNumber;
??? }
??? /**
???? * Adds the first number and second number together.
???? *
???? * @return next logical outcome.
???? */
??? public String add() {
???????
??????? result = calculator.add(firstNumber, secondNumber);
??????? return "success";
??? }
??? /**
???? * Multiplies the first number and second number together.
???? *
???? * @return next logical outcome.
???? */
??? public String multiply() {
??????? result = calculator.multiply(firstNumber, secondNumber);
??? ???
??????? return "success";
??? }
}
請(qǐng)注意,在Listing 2中乘法和加法都返回“success”。“success”表明一個(gè)邏輯輸出,它不是保留字,它是與faces-config.xml的導(dǎo)航規(guī)則相結(jié)合的,通過(guò)增加或定義操作執(zhí)行,頁(yè)面將轉(zhuǎn)向result.jsp。?這樣,你已經(jīng)完成了后臺(tái)代碼工作。接下來(lái)我們將定義JSP頁(yè)面及組件樹(shù)以展示應(yīng)用視圖。
?創(chuàng)建index.jsp頁(yè)面
?Index.jsp的目的是保證/calculator.jsp頁(yè)面被正確的裁入JSF內(nèi)容中,使得頁(yè)面可以找到正確的根視圖。
<jsp:forward page="/calc/calculator.jsp" />?這個(gè)頁(yè)面全部所做的工作便是將用戶(hù)重定向到/calc/calculator.jsp頁(yè)面。這樣便將calculator.jsp頁(yè)面導(dǎo)向JSF內(nèi)容中,使得JSF可以找到根視圖。
?創(chuàng)建calculator.jsp頁(yè)面
?Calculator.jsp是整個(gè)計(jì)算器應(yīng)用程序的中心視圖。這個(gè)頁(yè)面獲取用戶(hù)輸入的兩個(gè)數(shù)字,如Figure
3所示。
Figure 3. The Calculator
page

?
這一頁(yè)較為復(fù)雜,我將一步一步進(jìn)行講解。首先你需要聲明供JSF使用的標(biāo)簽庫(kù):
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
上述代碼告訴JSP引擎你將使用兩個(gè)JSF標(biāo)簽庫(kù):html和core。Html標(biāo)簽庫(kù)包含所有與forms和其它HTML規(guī)范有關(guān)的標(biāo)簽。Core標(biāo)簽庫(kù)包含所有邏輯、校驗(yàn)、控制等JSF標(biāo)簽。?一旦將頁(yè)面展示在普通的HTML中,你會(huì)告訴JSF系統(tǒng)你將使用JSF來(lái)管理你的組件。通過(guò)使用<f:view>標(biāo)簽來(lái)實(shí)現(xiàn)這點(diǎn),它會(huì)告知容器你將使用JSF來(lái)管理所有包含在它里面的組件。
?少了<f:view>,JSF將無(wú)法構(gòu)建組件樹(shù),稍后也無(wú)法在已經(jīng)創(chuàng)建起來(lái)的組件樹(shù)中進(jìn)行查詢(xún)。通過(guò)以下代碼使用<f:view>:
<f:view>
? <h:form id="calcForm">
???? ...?? ?
? </h:form>
</f:view>
上面的第一行是<f:view>的聲明,告知容器它受管于JSF。下一行是<h:form>標(biāo)簽,它告知JSF你需要在此建一個(gè)HTML FORM。在位于FORM組件內(nèi)的組件容器的語(yǔ)法渲染期間,所有的組件將會(huì)被問(wèn)詢(xún)自動(dòng)渲染,這樣它們將會(huì)生成標(biāo)準(zhǔn)的HTML代碼。
接下來(lái),你告知JSF在FORM里所需的其它組件。在<h:form>中定義了一個(gè)panelGrid。panelGrid是一個(gè)組合組件——也就是一個(gè)組件里包含有其它的組件。panelGrid定義其它組件的布局,Listing 3顯示如何定義panelGrid的代碼:
Listing 3. Declaring the
panelGrid
<h:panelGrid columns="3">
? <h:outputLabel value="First Number" for="firstNumber" />
? <h:inputText id="firstNumber" value="#{CalcBean.firstNumber}" required="true" />
? <h:message for="firstNumber" />???
? <h:outputLabel value="Second Number" for="secondNumber" />
? <h:inputText id="secondNumber" value="#{CalcBean.secondNumber}" required="true" />
? <h:message for="secondNumber" />
</h:panelGrid>屬性columns被定義為3表明所有組件將被布置在擁有3列空間的格局中。我們?cè)?/span>panelGrid中加入了6個(gè)組件,共占2行。每一行包含一個(gè)outputLabel,一個(gè)inputText和一條message。Label和message都和inputText組件關(guān)聯(lián),因此當(dāng)一個(gè)校驗(yàn)錯(cuò)誤或或誤信息關(guān)聯(lián)到textField時(shí),信息將會(huì)顯示在message組件上。兩個(gè)文本輸入域都要求有,如果在提交的時(shí)候檢測(cè)到無(wú)值,錯(cuò)誤信息將會(huì)被創(chuàng)建,控制也將會(huì)轉(zhuǎn)到這個(gè)視圖來(lái)。
注意到兩個(gè)inputFields都使用一條JSF表達(dá)式來(lái)做數(shù)值綁定。乍一看這很像一條JSTL表達(dá)式。但是,JSF表達(dá)式確實(shí)與綁定著后臺(tái)代碼相應(yīng)字段的輸入域相關(guān)聯(lián)。這種關(guān)聯(lián)是反向的,如果firstNumber是100,那么在form顯示的時(shí)候100也會(huì)被顯示。同樣,如果用戶(hù)提交一個(gè)有效的值,例如200,那么200也將作為firstNumber的新值。
一個(gè)更加實(shí)用的目的是,后臺(tái)代碼通過(guò)綁定模型對(duì)象的屬性的值到輸入域中,從而將模型對(duì)象展現(xiàn)出來(lái)。你將在此節(jié)稍后看到關(guān)于此目的的例子。
除了輸入域,calForm通過(guò)panelGroup中的兩個(gè)commandButton與兩個(gè)動(dòng)作關(guān)聯(lián):
<h:panelGroup>
??? <h:commandButton id="submitAdd" action="#{CalcBean.add}"? value="Add" />
??? <h:commandButton id="submitMultiply" action="#{CalcBean.multiply}" value="Multiply" />
</h:panelGroup>
panelGroup的概念與panelGrid很相似,除了它們顯示方式的不同。命令按鈕利用action=”#{CalcBean.add}”將按鈕與后臺(tái)動(dòng)作綁定在一起。因此當(dāng)這個(gè)form通過(guò)這個(gè)按鈕提交的時(shí)候,相關(guān)聯(lián)的方法便開(kāi)始執(zhí)行。
創(chuàng)建results.jsp頁(yè)面
Results.jsp頁(yè)面是用來(lái)顯示最后計(jì)算結(jié)果。如Listing 4所示:
Listing 4. The
results.jsp page
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
...
<f:view>
? First Number: <h:outputText id="firstNumber" value="#{CalcBean.firstNumber}"/>
? <br />
? Second Number: <h:outputText id="secondNumber" value="#{CalcBean.secondNumber}"/>
? <br />
? Result: <h:outputText id="result" value="#{CalcBean.result}"/>
? <br />
</f:view>
Results.jsp是一個(gè)相對(duì)簡(jiǎn)單的頁(yè)面,將加法運(yùn)算的結(jié)果顯示給用戶(hù)。通過(guò)<h:outputText>標(biāo)簽來(lái)完成這一功能。<h:outputText>標(biāo)簽帶有id和value屬性,value屬性輸出值,它在渲染的時(shí)候?qū)⒈划?dāng)作string看待。Value屬性通過(guò)JSF將要輸出的值與后臺(tái)代碼的屬性綁定在一起。?運(yùn)行程序!
運(yùn)行war文件所映射的程序。Index.jsp頁(yè)面將調(diào)用calculator.jsp,如果你在firstNumber域或secondNumber域中輸入非法文本(如“abc”)提交,你將返回到/calculator.jsp視圖并且相應(yīng)的錯(cuò)誤信息將會(huì)顯示在頁(yè)面上。如果你將firstNumber或secondNumber放空提交你也將會(huì)獲得相應(yīng)的錯(cuò)誤結(jié)果。由此可以看出在JSF許多校驗(yàn)幾乎是自動(dòng)定義,只要你將輸入域定義為required并且綁定相應(yīng)的int屬性的字段即可。
Figure 4顯示應(yīng)用程序如何處理校驗(yàn)和數(shù)據(jù)轉(zhuǎn)換數(shù)據(jù)。
Figure 4. Validation and
data conversion errors

?
總結(jié)
在這篇關(guān)于JSF的介紹中是否使你有些頭暈?不用擔(dān)心,你已經(jīng)跨過(guò)了最壞的一道坎。了解JSF的框架概念是這場(chǎng)戰(zhàn)役的一半,有過(guò)之而無(wú)不及,而且你將會(huì)很快意識(shí)到它的價(jià)值。
如果在閱讀的過(guò)程中你想象使用Struts實(shí)現(xiàn)上述代碼會(huì)更加簡(jiǎn)單,我估計(jì)它會(huì)耗費(fèi)至少兩倍精力。用Struts構(gòu)建同樣的應(yīng)用,你需要為兩個(gè)按鈕創(chuàng)建兩個(gè)action類(lèi),各自需要自己的對(duì)應(yīng)的動(dòng)作映射。而且需要一個(gè)動(dòng)作映射來(lái)裝載首頁(yè)(假設(shè)你遵守Model 2的建議)。另外,模仿JSF默認(rèn)的錯(cuò)誤獲取和校驗(yàn)機(jī)制,你需要為Struts配置校驗(yàn)框架或者實(shí)現(xiàn)在ActionForm中實(shí)現(xiàn)等值的validate方法。你還必須在Struts配置中定義DynaValidatorForm或者建立一個(gè)ActionForm重寫(xiě)validate方法,或者使用ValidatorForm的子類(lèi)。最終,你或許(必須)需要為所有的action定義forward或者全局forward。
不止雙倍的代碼工作,Struts對(duì)于初學(xué)者來(lái)說(shuō)意味著需要花費(fèi)更多的精力。我之所以知道這點(diǎn),是因?yàn)槲覍?xiě)過(guò)關(guān)于Struts和JSF課程的教材并且為我的學(xué)員們上過(guò)課。開(kāi)發(fā)人員通常非常容易掌握JSF,但在學(xué)習(xí)Struts的過(guò)程中卻倍受折磨。我相信更多有遠(yuǎn)見(jiàn)的人選擇JSF,而非Struts。直覺(jué)上說(shuō),JSF更加合理。Struts已經(jīng)被擱置,JSF被列入技術(shù)清單。在我的書(shū)中,JSF開(kāi)發(fā)過(guò)程是簡(jiǎn)便的,并且比Struts更具有生產(chǎn)率。
這是JSF系列的第一篇文章的總結(jié)。在下一篇文章里我會(huì)繼續(xù)這篇文章的話(huà)題,內(nèi)容覆蓋JSF的request處理生命周期,指明生命周期中同一應(yīng)用程序的不同部分。我還將介紹immediate
event handling的概念,并且讓你對(duì)JSF的組件事件模型有更全面了解,包括關(guān)于許多內(nèi)嵌組件的討論。我還將談?wù)動(dòng)嘘P(guān)JSF與JavaScript相結(jié)合的話(huà)題,請(qǐng)關(guān)注下個(gè)月的文章。