JSF 生命周期:概述
JSF 程序生命周期的 5 個(gè)階段如下(注意每個(gè)階段的事件處理):
這 5 個(gè)階段顯示了 JSF 通常處理 GUIi 的順序。雖然這個(gè)清單列出了每個(gè)階段中事件處理的可能執行順序,但是 JSF 的生命周期很難是固定一成不變的。您可以通過(guò)忽略某個(gè)階段或合并整個(gè)生命周期從而對執行順序進(jìn)行修改。例如,如果一個(gè)無(wú)效的請求值被拷貝到一個(gè)組件中,那么當前的視圖就會(huì )重新顯示,而有些階段就可能不會(huì )執行。在這種情況中,您可以執行一個(gè) FacesContext.responseComplete 方法調用,將用戶(hù)重定向到一個(gè)不同的頁(yè)面上,然后使用請求分發(fā)器(從 FacesContext 中的請求對象中獲得)將其轉發(fā)到一個(gè)適當的 Web 資源上。另外,您可以調用 FacesContext.renderResponse 重新顯示原來(lái)的視圖。(詳細信息請參看下面的示例程序。)
關(guān)鍵是讓生命周期構成您的開(kāi)發(fā)項目,而不完全依賴(lài)于生命周期。在需要時(shí),您可以修改生命周期,而不用擔心破壞您的程序。在大部分情況中,您會(huì )發(fā)現 JSF 的生命周期是值得遵守的,因為它的邏輯非常好。表單必須在任何應用程序邏輯執行之前進(jìn)行驗證,并且在進(jìn)行驗證之前,必須對域中的數據進(jìn)行轉換。遵守生命周期的規定,可以讓您更自由地考慮有關(guān)驗證和轉換的問(wèn)題,而不是請求處理本身的階段。有一點(diǎn)非常重要:其他 Web 框架也都具有類(lèi)似的生命周期;它們只不過(guò)是沒(méi)有很好地進(jìn)行宣傳。
專(zhuān)注
有些使用 JSF 的開(kāi)發(fā)者可能從來(lái)都不會(huì )編寫(xiě)一個(gè)組件,也不會(huì )對框架進(jìn)行任何擴展;而另外一些人則專(zhuān)注于這種任務(wù)的開(kāi)發(fā)。盡管 JSF 的生命周期與大部分那其他項目都是相同的,但是根據在項目中的角色您可以采用不同的階段。如果您更專(zhuān)注于通用的應用程序開(kāi)發(fā),就可能會(huì )關(guān)注請求處理生命周期的中間階段:
如果您專(zhuān)注于 JSF 組件的開(kāi)發(fā),就可能會(huì )關(guān)注于整個(gè)生命周期中的第一個(gè)階段和最后一個(gè)階段:
在接下來(lái)的幾節中,我們將遍歷 JSF 請求處理生命周期的每個(gè)步驟,包括事件處理和驗證。了解了每個(gè)步驟的基本知識之后,我們將簡(jiǎn)要介紹一個(gè)示例程序,它可以展示這些步驟如何一起使用。在開(kāi)始之前,首先來(lái)看一下圖 1,這是一個(gè)有關(guān) JSF 生命周期的圖。
圖 1. JSF 生命周期
階段 1:恢復視圖
在 JSF 生命周期的第一個(gè)階段 ——恢復視圖 —— 中,會(huì )有一個(gè)來(lái)自 FacesServlet 控制器的請求??刂破鲿?huì )對請求進(jìn)行考查,并提取出視圖的 ID,這是由 JSPi 頁(yè)面的名字來(lái)確定的。
JSF 框架控制器使用這個(gè)視圖 ID 來(lái)為當前的視圖查找組件。如果這個(gè)視圖尚未存在,那么 JSF 控制器就會(huì )創(chuàng )建它。如果這個(gè)視圖早已存在,那么 JSF 控制器就會(huì )使用它。這個(gè)視圖包含了所有的 GUI 組件。
生命周期的這個(gè)階段表示為三個(gè)視圖實(shí)例:新視圖、原始視圖和后視圖,每個(gè)視圖的處理方式都不相同。在 新視圖 的情況中,JSF 會(huì )構建 Faces 頁(yè)面的視圖,并將事件處理程序和驗證程序綁定到組件上。這個(gè)視圖被保存在一個(gè) FacesContext 對象中。
FacesContext 對象包含了 JSF 用來(lái)管理當前會(huì )話(huà)中當前請求的 GUI 組件狀態(tài)所需要的所有狀態(tài)信息。FacesContext 將視圖保存在自己的 viewRoot 屬性中;viewRoot 包含了當前視圖 ID 的所有 JSF 組件。
在 原始視圖 的情況中(第一次加載的是一個(gè)頁(yè)面),JSF 會(huì )創(chuàng )建一個(gè)空視圖。這個(gè)空視圖會(huì )在用戶(hù)事件產(chǎn)生時(shí)進(jìn)行填充。JSF 可以直接從原始視圖過(guò)渡到進(jìn)行響應的階段。
在 后視圖(postback) 的情況中(用戶(hù)返回之前訪(fǎng)問(wèn)過(guò)的頁(yè)面),包含頁(yè)面的視圖早已經(jīng)存在了,因此只需要進(jìn)行恢復就可以了。在這種情況中,JSF 就使用現有視圖的狀態(tài)信息來(lái)重構狀態(tài)。后視圖的下一個(gè)階段是應用請求值。
階段 2:應用請求值
應用請求值 階段的目的是讓每個(gè)組件檢索自己當前的狀態(tài)信息。這些組件必須首先通過(guò) FacesContext 對象進(jìn)行檢索或創(chuàng )建(使用其值)。雖然組件值也可以從 cookie 或頭文件中進(jìn)行檢索,但是它們通常是通過(guò)請求參數進(jìn)行檢索的。
如果一個(gè)組件的即時(shí)事件處理屬性 沒(méi)有 設置為 true,那么就會(huì )對這些值進(jìn)行轉換。因此,如果 域 被綁定到一個(gè) Integer 屬性上,那么該值就會(huì )被轉換為一個(gè) Integer 類(lèi)型。如果值的轉換失敗了,那么就會(huì )生成一個(gè)錯誤消息,并在 FacesContext 中進(jìn)行排隊,在產(chǎn)生響應的階段會(huì )顯示其中的消息,同時(shí)還會(huì )顯示所有的驗證錯誤。
如果一個(gè)組件的即時(shí)事件處理屬性 的確 被設置為 true,那么這些值就會(huì )被轉換為適當的類(lèi)型,并進(jìn)行有效性驗證。然后轉換后的值會(huì )被保存到組件中。如果值轉換或值的有效性驗證失敗了,就會(huì )生成一個(gè)錯誤消息,并在 FacesContext 中進(jìn)行排隊,在產(chǎn)生響應的階段會(huì )顯示其中的消息,同時(shí)還會(huì )顯示所有的驗證錯誤。
處理驗證
生命周期中的第一個(gè)事件處理發(fā)生在應用請求值階段之后。在這個(gè)階段中,每個(gè)組件都有一些值需要根據應用程序的驗證規則進(jìn)行有效性驗證。這些驗證規則可以是預先進(jìn)行定義的(JSF 中提供的),也可以由開(kāi)發(fā)者進(jìn)行定義。用戶(hù)所輸入的值會(huì )與這些驗證規則進(jìn)行比較。如果說(shuō)輸入的值無(wú)效,就會(huì )向 FacesContext 中添加一個(gè)錯誤消息,并且該組件會(huì )被表示為無(wú)效的。如果一個(gè)組件被表示為無(wú)效的,那么 JSF 就會(huì )轉到產(chǎn)生響應的階段,在這個(gè)階段中會(huì )顯示當前的視圖,以及驗證錯誤消息。如果沒(méi)有有效性驗證錯誤,那么 JSF 就會(huì )轉到更新模型值的階段。
階段 3:更新模型值
JSF 應用程序生命周期中的第三個(gè)階段 ——更新模型值 —— 負責更新服務(wù)器端模型的實(shí)際值,通常來(lái)講,這都是通過(guò)更新后臺 bean(稱(chēng)為管理 bean)的屬性實(shí)現的。只有那些與組件值綁定在一起的 bean 屬性才會(huì )被更新。注意這個(gè)階段發(fā)生在有效性驗證之后,因此可以確??截惖?bean 屬性的值都是有效的(至少在表單域一級都是有效的;在業(yè)務(wù)規則一級仍可能無(wú)效)。
階段 4:調用程序
在生命周期的第四個(gè)階段 ——調用程序 —— 中,JSF 控制程序會(huì )調用程序來(lái)處理 表單 的提交操作。組件值已經(jīng)經(jīng)過(guò)了類(lèi)型轉換和有效性驗證,并被應用到模型對象中了,因此您現在可以使用它們來(lái)執行應用程序的業(yè)務(wù)邏輯了。
在這個(gè)階段,您還可以為一個(gè)給定的序列或很多可能的序列指定后面的邏輯視圖,這可以通過(guò)為一次成功的表單提交定義一個(gè)特定的結果并返回這個(gè)結果來(lái)實(shí)現。例如:在成功輸出時(shí),將用戶(hù)重定向到下一頁(yè)中。要讓這種導航工作能夠起作用,您需要在 faces-config.xml 文件中創(chuàng )建一個(gè)到 成功輸出 的映射作為一條導航規則。一旦導航發(fā)生之后,您就轉換到生命周期的最后一個(gè)階段了。
階段 5:進(jìn)行響應
在生命周期的第五個(gè)階段 ——進(jìn)行響應 —— 中,您可以在視圖中顯示當前狀態(tài)中的所有組件。
圖 2 是 JSF 生命周期的第五個(gè)階段的一個(gè)對象狀態(tài)圖,包括時(shí)間有效性驗證和處理。
圖 2. JSF 生命周期的五階段
范例
現在您已經(jīng)對 JSF 生命周期的階段有了基本的了解,下面我們將向您介紹在一個(gè)范例 Web 應用程序中,這些階段是如何協(xié)同工作的。除了展示 JSF 生命周期的基本功能之外,這個(gè)應用程序還會(huì )利用一些通用的 JSF GUI 組件,例如 Radio List, List, Text Field, Label, Panel 等等,這樣您就可以親自體驗一下在 第 1 部分 中曾經(jīng)簡(jiǎn)要討論過(guò)的這些組件。
這個(gè)示例程序還會(huì )展示在 JSF 中使用其他 Javai 技術(shù)的兩種方法。它將組合使用 JSF 和 JavaScript 來(lái)啟用即時(shí)事件處理(在那些對整個(gè)表單進(jìn)行驗證是多余的情況中),其布局是由 Struts Tiles 進(jìn)行管理的。雖然 Struts Tiles 并不是 JSF 的一個(gè)必要部分,但是 tiles 通常用來(lái)為一個(gè)程序中的所有 JSF 頁(yè)面提供一致的外觀(guān)。
程序設置
這個(gè)示例 Web 程序實(shí)際上是一個(gè)非常簡(jiǎn)單的創(chuàng )建、閱讀、更新并刪除(CRUD)一個(gè)在線(xiàn) CD 倉庫中庫存的程序。它包括一個(gè)表單,讓用戶(hù)可以向系統中輸入新 CD;有一些單選按鈕,讓用戶(hù)選擇音樂(lè )的分類(lèi)。當用戶(hù)選擇一個(gè)分類(lèi)時(shí),就啟動(dòng)一些 JavaScript 腳本將這個(gè)表單立即發(fā)回服務(wù)器。程序組合采用 JSF 和 JavaScript 技術(shù)來(lái)處理一個(gè)組件,而不是整個(gè)頁(yè)面,這種技術(shù)稱(chēng)為 即時(shí)事件處理。在這種情況中,您可以填充一個(gè)子類(lèi)清單,而不用驗證表單的其他內容。
這個(gè)示例程序還包括一個(gè) CD 清單,它將展示如何使用 JSF 的 dataTable。從這個(gè)頁(yè)面中,最終用戶(hù)可以根據標題或者藝術(shù)家對 CD 清單進(jìn)行排序。
類(lèi)和方法
StoreManagerDelegate 類(lèi)是這個(gè)程序的業(yè)務(wù)代表。它為整個(gè)模型呈現了主界面。CD 類(lèi)是一個(gè)數據轉換對象(DTO)。如果這是一個(gè)真實(shí)的程序,那么 StoreManagerDelegate 類(lèi)就會(huì )為添加、刪除和編輯 CD 實(shí)現所有的業(yè)務(wù)規則,還會(huì )負責使用一個(gè)數據訪(fǎng)問(wèn)對象(DAO)將 CD 存儲到一個(gè)永久的存儲介質(zhì)中。StoreManagerDelegate 和 CD 包含了一些用于這個(gè) MVCi 程序的 模型。
StoreController 類(lèi)是本例中的主要后臺 bean。StoreController 類(lèi)是 GUI 世界和模型世界之間的黏合劑。它將自己的很多行為都委托給 StoreManagerDelegate 進(jìn)行處理。StoreController 是這個(gè) MVC 程序的 控制程序。
StoreController 類(lèi)展示了如何構建一個(gè)可排序的 CRUD 清單。它具有以下與 CRUD 相關(guān)的方法:editCD、addNew、addCD 以及 updateCD。StoreController 還負責將模型對象呈現給表單。在這種情況中,它使用 cd 屬性將當前的 CD 對象呈現給 CD 表單,該屬性的類(lèi)型就是 CD。
開(kāi)始編碼
開(kāi)始編寫(xiě)這個(gè)示例程序的最好方法是遍歷它的使用案例:
第三個(gè)使用案例和第四個(gè)使用案例的代碼基本上是相同的,因此我將向您展示如何根據標題進(jìn)行排序,并將第四個(gè)使用案例留作練習,請您自行完成。我們很快就會(huì )對使用案例進(jìn)行編碼,但是首先讓我們來(lái)了解一下完成后的應用程序的頁(yè)面將是什么樣子。
使用案例 1:新增 CD
在該程序的第一個(gè)使用案例中,用戶(hù)將添加一個(gè)新 CD:切換到 CD 清單頁(yè)面上,點(diǎn)擊 Add CD 鏈接(這是在 listing.jsp 文件中定義的),如清單 1 所示。
|
這個(gè)鏈接被綁定到 CDManagerBean 的 addNew 方法上。這個(gè) addNew 方法在 JSF 生命周期的調用程序階段(最后一個(gè)階段)被調用的。操作被使用 JSF 綁定表達式 #{CDManagerBean.addNew} 綁定到這個(gè)方法上。CDManagerBean 是這個(gè)程序的存儲控制器的一個(gè)別名。CDManagerBean 是這個(gè)控制器的邏輯名??刂破黝?lèi)是一個(gè)在 faces-config.xml 文件中定義的管理 bean,如清單 2 所示。
|
準備表單addNew() 方法通過(guò)創(chuàng )建一個(gè)空 CD 來(lái)準備表單,如清單 3 所示。
|
addNew() 方法通過(guò)創(chuàng )建一個(gè)新的 CD 來(lái)清空 CD 表單域。這個(gè) CD 表單的域被綁定到 cd 屬性的屬性中。這個(gè)方法還會(huì )將正在顯示的子類(lèi)清單置空。
返回成功結果
接下來(lái),addNew() 方法會(huì )被調用,控制權被重定向到成功映射頁(yè)面,即 cdForm.jsp 文件。cdForm.jsp 文件是在 faces-config.xml 文件中定義的,如清單 4 所示。
|
清單 4 表明如果用戶(hù)從清單切換到 addNew (#{CDManagerBean.addNew}) 操作,并且 addNew 操作成功返回,那就會(huì )切換到 cdForm.jsp 頁(yè)面。
設置 cdForm 和 panelGrid
cdForm.jsp 是包含 CD 表單的表單。其中具有 ID、Title、Artist、Price、Category 和 Subcategory 的域。這些域被放到一個(gè)名為 panelGrid 的容器中。JSF 組件,例如 AWTi 組件,具有一些容器和組件。容器 是一個(gè)包含其他組件的組件。這是一個(gè) 混合設計模式 的例子。panelGrid 有 3 列。每個(gè)域都各在一行中,還會(huì )有一個(gè)標簽和消息用于顯示該域的錯誤消息。cdForm 和 panelGrid 是在清單 5 中定義的。
|
關(guān)于代碼的注釋
每個(gè)輸入域都將該域綁定到控制器的 cd 屬性的一個(gè)屬性上。例如,標題的輸入文本域被使用下面的 JSF 綁定表達式綁定到 cd 屬性上:value="#{CDManagerBean.cd.title}"。
您可能會(huì )注意到在清單 5 中幾乎沒(méi)有什么 HTMLi 語(yǔ)句。這是由于 panelGrid 會(huì )生成大部分的 HMTL 語(yǔ)句。注意實(shí)際的外觀(guān)是由與 panelGrid 相關(guān)的樣式表決定的。屬性 rowClasses="row1, row2" 會(huì )為正在修改的行設置 CSSi 類(lèi)。第一行是白色的,第二行是灰色的。您還可以為列或其他內容指定 CSS 類(lèi)。JSF panelGrid 組件可以方便地快速設置表單的布局。如果您希望實(shí)現 panelGrid 沒(méi)有提供的功能,就不能使用它:不過(guò)可以使用 HTML 自己設置組件的布局。然而,如果您發(fā)現自己在很多頁(yè)面上都使用了定制的 HTML,那么就可能會(huì )考慮編寫(xiě)自己的定制組件。這種想法可以讓您盡可能 DRY 地重用 HTML 語(yǔ)句(DRY 是 don‘t repeat yourself 的縮寫(xiě),這個(gè)術(shù)語(yǔ)來(lái)自于 Dave Thomas 的 Pragmatic Programmer 一書(shū))。
關(guān)于清單 5 另外需要注意的是控制器有一個(gè) editMode 屬性,由 cdForm.jsp 用于有選擇地顯示 submitAdd 按鈕或 submitUpdate 按鈕;submitAdd 按鈕是在表單不處于編輯模式時(shí)顯示的。submitUpdate 按鈕是在表單處于編輯模式時(shí)顯示的。這可以簡(jiǎn)化為編輯和添加模式使用相同的 JSP。(默認情況下,表單不處于編輯模式。)這種功能是由 cdForm.jsp 中的每個(gè)按鈕上的呈現表達式實(shí)現的。例如,清單 6 列出了 submitAdd button rendered="#{not CDManagerBean.editMode}" 上的呈現表達式。submitAdd 按鈕被使用表達式 (action="#{CDManagerBean.addCD}") 綁定到 addCD 方法上。
|
對域進(jìn)行有效性驗證
在 addCD 方法被調用之前,JSF 必須對 GUI 中的域進(jìn)行有效性驗證。這實(shí)際上非常簡(jiǎn)單,因為您還沒(méi)有為域關(guān)聯(lián)任何有效性驗證條件。在應用請求值階段,這些值被從請求參數拷貝到組件值中(這是由組件本身進(jìn)行的)?,F在,價(jià)格從一個(gè)字符串轉換為一個(gè)浮點(diǎn)類(lèi)型。如果用戶(hù)為價(jià)格輸入的是“abc”,那么轉換為浮點(diǎn)類(lèi)型的操作就會(huì )失敗,控制權將被重新定向到 cdForm.jsp 頁(yè)面上,供最終用戶(hù)進(jìn)行修正。與價(jià)格相關(guān)的 h:message 將顯示一個(gè)轉換錯誤消息。如果所有的值都可以正常進(jìn)行類(lèi)型轉換,并且現在都可以使用了(如果需要的話(huà)),那么您就可以進(jìn)行有效性驗證的處理了。由于這個(gè)示例程序并沒(méi)有與組件關(guān)聯(lián)任何有效性驗證規則(在下一篇文章中我們將介紹這種特性),因此您可以繼續進(jìn)入更新模型值的階段了。
在更新模型值的階段中,會(huì )使用保存在 GUI 組件中的經(jīng)過(guò)轉換和有效性驗證的值來(lái)調用 CD 的賦值方法。addCD() 方法是在 調用程序 階段中被調用的。addCD() 方法使用一個(gè)業(yè)務(wù)代理(store 對象)來(lái)執行這個(gè)操作。addCD 方法在系統中使用 store 對象來(lái)存儲 CD。由于 addCD 方法會(huì )返回成功,因此接下來(lái)會(huì )顯示這個(gè)清單,這是在 faces-config.xm 中定義的。在 faces-config.xml 中定義的導航規則如清單 7 所示。
|
使用案例 2:編輯 CD
這個(gè)示例程序的第二個(gè)使用案例也會(huì )在這個(gè)清單頁(yè)面(listing.jsp)中啟動(dòng)。除了向您介紹如何編輯 JSF 頁(yè)面中的數據之外,這個(gè)使用案例還將向您介紹 JSF dataTable 組件。
這個(gè)清單頁(yè)面使用一個(gè) dataTable 組件來(lái)顯示 CD 的清單。dataTable 的值被綁定到控制程序類(lèi) StoreController 的 cds 屬性。cds 屬性的定義如清單 8 所示。
|
cds 屬性是基于從存儲對象 StoreManagerDelegate 返回的 java.util.List 的,這個(gè)對象是該程序的業(yè)務(wù)代理。cdModel 對從 DataModel 中的存儲對象返回的清單進(jìn)行了封裝。DataModel 是一個(gè)用于 dataTable 的模型。
dataTable 的定義如清單 9 所示。
|
注意該值被綁定到控制程序的 cds 屬性上。rowClasses 和 headerClass 屬性用來(lái)指定 CSS 類(lèi),后者用來(lái)定義 dataTable 的外觀(guān)。正如前面介紹的一樣,JSF 嚴重依賴(lài)于 CSS 來(lái)定義 GUI 的外觀(guān)。如果您并不了解 CSS(即您之前都是使用字體標簽和 HTML 表來(lái)設置外觀(guān)的),就可能會(huì )希望在靈活運行 JSF 之前首先來(lái)學(xué)習一下有關(guān) CSS 的知識。
column 組件Title、Artist 和 Price 域都是使用 column 組件顯示的,如清單 10 所示(此處只顯示了 Title 域)。
|
column 組件是 dataTable 的一個(gè)子組件。column 組件使用一個(gè)子組件和一個(gè) facet。facet 是一個(gè)有名的子組件;它并不是一個(gè)子孫組件。column 組件有一個(gè)名為 header 的 facet,它定義了在 header 中顯示的內容。對于本例來(lái)說(shuō),commandLink 是 column 組件的一個(gè)子孫組件。commandLink 在一個(gè)鏈接中顯示了 CD 的標題,該鏈接被綁定到操作 #{CDManagerBean.editCD} 上。這個(gè)操作屬性將 commandLink 綁定到控制程序類(lèi)的 editCD() 方法上,如清單 11 所示。
|
editCD() 方法editCD() 方法是在 JSF 生命周期的調用程序階段調用的。editCD() 方法準備控制程序以使用編輯模式來(lái)顯示 cdForm.jsp 頁(yè)面。這是通過(guò)查看當前選定的 CD 來(lái)實(shí)現的,CD 是通過(guò)調用 cdModel.getRowData() 方法來(lái)選擇的。
注意 JSF DataModel 允許您從比傳統的 Web 應用程序更高的層次上使用數據。您并不需要對請求參數進(jìn)行檢查:只需要調用 cdModel.getRowData() 方法向 DataModel(cdModel)查詢(xún)已經(jīng)選擇了哪個(gè) CD。這個(gè)更高級別的抽象對 Web 開(kāi)發(fā)進(jìn)行了相當程度的簡(jiǎn)化。
一旦取得當前選擇的 CD 之后,就可以使用業(yè)務(wù)代理來(lái)加載該 CD 的最新拷貝了(store.getCDById())。在加載這個(gè) CD 之后,store.getCDById() 會(huì )激活 subCategory 清單(假設這個(gè) CD 已經(jīng)關(guān)聯(lián)了一個(gè)子目錄),然后將 editMode 屬性設置為 true?;叵胍幌?,editMode 屬性是由 cdForm 用來(lái)顯示 Add 或 Update 按鈕。最后,store.getCDById() 方法返回 success。在清單 12 中重要的導航規則可以保證返回成功之后,切換到 cdForm.jsp 頁(yè)面,如下所示。
|
updateCD() 方法
CD 表單會(huì )加載并顯示 CD 屬性的屬性設置。最終用戶(hù)可以根據需要編輯所得到的表單,并在完成時(shí)點(diǎn)擊 Update 按鈕。Update 按鈕是當用戶(hù)處于 Edit 模式時(shí)所顯示的惟一一個(gè)按鈕,它只會(huì )在 editMode 為 true 時(shí)顯示,如清單 13 所示。
|
Update 按鈕被綁定到 updateCD() 方法上。在調用 update 方法之前,JSF 必須對 GUI 中的域進(jìn)行有效性驗證。在應用請求值階段,這些值被從請求參數中拷貝到組件值中(這是由組件本身完成的)?,F在,價(jià)格被從一個(gè)字符串轉換成了一個(gè)浮點(diǎn)類(lèi)型。由于沒(méi)有為組件關(guān)聯(lián)任何有效性驗證規則,因此如果所有請求的值都已經(jīng)存在并經(jīng)過(guò)轉換了,就可以轉換到生命周期的下一個(gè)步驟了。
更新模型值
在更新模型值階段中,會(huì )使用保存在 GUI 組件中經(jīng)過(guò)類(lèi)型轉換和有效性驗證的值來(lái)調用 CD 的賦值函數。updateCD() 方法是在調用程序階段被調用的。updateCD() 方法如清單 14 所示。
|
updateCD() 方法可以代理業(yè)務(wù)代理的大部分職責。它將 editMode 設置為 false(這是默認值),并返回成功。成功輸出將您重定向回清單頁(yè)面中,在這個(gè)頁(yè)面中您可以查看根據清單 15 中顯示的導航規則新編輯的 CD。
|
使用案例 3:對 CD 進(jìn)行排序
我們要介紹的最后一個(gè)使用案例將向您展示如何對表進(jìn)行排序。這個(gè)使用案例也是在 CD 清單頁(yè)面上啟動(dòng)的。清單頁(yè)允許根據標題和藝術(shù)家對 CD 按照升序或降序的順序進(jìn)行排列。在本例中,我將向您展示如何根據標題進(jìn)行排序,并將根據藝術(shù)家進(jìn)行排序留作練習。
標題頭排序有一些到控制程序中排序方法的鏈接。清單 16 顯示了在 listing.jsp 中是如何顯示標題頭的。
清單 16. 對 commandLinks 進(jìn)行排序 |
panelGroup 組件
注意一下清單 16,鏈接是在標題列的 header facet 中定義的。facet 只會(huì )關(guān)聯(lián)一個(gè)惟一名字的組件;這樣,要在 header facet 中放置一個(gè)多鏈接的組件,您需要使用 panelGroup。panelGroup (與 panelGrid 類(lèi)似)是一個(gè)單獨的組件,其中包含了很多子組件。panelGroup 包含兩個(gè)鏈接,如清單 17 所示。
|
第一個(gè)鏈接被綁定到控制程序的 sortTitleAsc 方法上,第二個(gè)鏈接被綁定到 sortTitleDec 上。這兩個(gè)方法如清單 18 所示。
|
這兩個(gè)方法都依賴(lài)于業(yè)務(wù)代理返回一個(gè)按照正確要求排序后的 java.util.List。注意這個(gè)方法會(huì )返回邏輯輸出 asc 和 dec。這兩個(gè)輸出在 faces-config.xml 文件中都沒(méi)有映射。沒(méi)有映射的輸出會(huì )導致重新加載當前的視圖;這樣,listing.jsp 將會(huì )在調用這些方法時(shí)重新進(jìn)行加載,清單頁(yè)面也會(huì )按照正確的順序重新顯示。
這種方法的優(yōu)點(diǎn)是它依賴(lài)于業(yè)務(wù)代理進(jìn)行排序。業(yè)務(wù)代理又可能會(huì )依賴(lài)于一個(gè) DAOi 對象,而后者又依賴(lài)于一個(gè)數據庫查詢(xún)或 OR 映射查詢(xún),這樣可以對 CD 進(jìn)行有效的查詢(xún)。這種方法通常比具有一個(gè)“智能” GUI 組件的方法更好,后一種方法知道如何對隨機的域對象(CD 就是一個(gè)域對象)進(jìn)行排序,因為排序操作是一個(gè)經(jīng)常發(fā)生的操作,嚴格來(lái)說(shuō),是模型的一部分(即域對象的一部分),而不是視圖的一部分。
正如前面介紹的一樣,對標題進(jìn)行排序和對藝術(shù)家進(jìn)行排序的代碼幾乎是相同的。作為一個(gè)練習,請自己試圖為第四個(gè)使用案例編寫(xiě)代碼,對藝術(shù)家而不是標題進(jìn)行排序。
即時(shí)事件處理
我們要介紹的最后一個(gè)主題是即時(shí)事件處理。即時(shí)事件處理在您不希望(或需要)對整個(gè)頁(yè)面進(jìn)行有效性驗證來(lái)處理用戶(hù)輸入的情況中非常有用?;叵胍幌?,示例程序的 cdForm.jsp 頁(yè)面使用單選按鈕來(lái)顯示一個(gè)目錄和子目錄清單。當最終用戶(hù)選擇一個(gè)目錄時(shí),cdForm.jsp 頁(yè)面就會(huì )使用 JavaScript 重新生成表單,這樣就可以顯示子目錄清單了。
這是一個(gè)即時(shí)事件處理的例子,因為整個(gè)表單 沒(méi)有 在調用事件處理程序之前進(jìn)行有效性驗證。相反,類(lèi)清單的事件處理程序會(huì )生成子目錄,并強制 JSF 跳過(guò)進(jìn)行響應的階段。組件的事件處理程序通常都是在調用程序階段執行的。即時(shí)事件組件的事件處理程序是在應用請求值階段執行的,這發(fā)生在其余組件的類(lèi)型轉換和有效性驗證之前。
清單 19 顯示了在 cdForm.jsp 頁(yè)面中再次顯示的目錄清單。
清單 19. cdForm.jsp 中的目錄清單 |
selectOneRadio 目錄域被綁定到 CD 的目錄屬性(value="#{CDManagerBean.cd.category}")上。注意這個(gè)即時(shí)事件處理被激活了(immediate="true")。這種設置意味著(zhù) Category 組件的事件會(huì )在應用值階段(而不是在調用程序階段)進(jìn)行處理(以及類(lèi)型轉換和有效性驗證)。
JavaScript 功能是在 onclick="submit()" 這一行 —— 即當用戶(hù)進(jìn)行修改時(shí),它應該立即被提交到 Web 程序中進(jìn)行處理。
事件處理程序方法
在清單中顯示的可用分類(lèi)是由 f:selectItems 標簽值(value="#{CDManagerBean.categories}")確定的。這個(gè)組件的事件處理程序的變化是控制程序的 categorySelected() 方法(valueChangeListener="#{CDManagerBean.categorySelected}")。事件處理程序如清單 20 所示。
|
categorySelected() 方法做的第一件事情是允許 subCategoryList 調用自己。categorySelected() 方法然后會(huì )使用所選擇的分類(lèi)值來(lái)查找一個(gè) subCategories 清單。subCategories 屬性被綁定到 subcategoryList 值上。接下來(lái),事件處理程序通過(guò)調用當前 FacesContext 上的 renderResponse() 方法強制 JSF 轉到進(jìn)行響應階段。然后,GUI(cdForm.jsp)為當前顯示的目錄重新顯示可用的子目錄。
將組件綁定到控制程序上subCategoryList 組件是從 GUI 上綁定的。正如您可以將值綁定到組件上一樣,您也可以將這些組件綁定到一個(gè)控制程序上。子目錄是在 cdForm.jsp 頁(yè)面中定義的,如清單 21 所示。
|
binding 屬性允許您將 GUI 的組件綁定到后端的 bean(控制程序)上。這樣,上面的組件就會(huì )被綁定到 CDManagerBean.subCategoryList 上,這是在清單 22 中定義的控制程序中的一個(gè)屬性。
|
即時(shí)事件處理只使用了很少的一點(diǎn) JavaScript 功能(onclick="submit()" 命令),這種靈活的 JSF 生命周期的便利可以讓您處理在一個(gè)組件中輸入的信息,而不用對整個(gè)頁(yè)面進(jìn)行有效性驗證。在這個(gè)例子中,我們已經(jīng)介紹了 Category 組件的即時(shí)處理是如何讓您可以顯示子目錄,而不用重新加載頁(yè)面的。
結束語(yǔ)
在本文中我們使用了一個(gè)范例和三個(gè)使用案例來(lái)介紹 JSF 請求處理的生命周期,并展示了其組件模型的一些必備特性。我們還介紹了如何組合使用 JSF 和 Struts Tiles,在 JSF 頁(yè)面之間實(shí)現更加統一的布局,如何使用 DataModel,以及如何組合使用 JSF 和 JavaScript 進(jìn)行即時(shí)事件的處理。
聯(lián)系客服