摘自:ChinaITLab | 作者: | 瀏覽率:70 |
JSF對通過關聯組件和事件來構建頁面而說是非常棒的,但是,與所有現有的技術一樣,它需要一個控制器來分離出頁面間的導航決策,并提供到業務層的鏈接。它擁有一個基本的導航處理程序,可以用功能完備的處理程序來替換它。Page Flow為創建可重用的封裝頁面流提供了基礎,并可以與視圖層并行工作。它是一個功能完備的導航處理程序,將JSF頁面作為最優先的處理對象。本文將討論如何集成這兩種技術來利用二者的優點。
構建Beehive/JSF應用程序要構建Beehive/JSF應用程序,首先要啟動Page Flow,然后添加對JSF的支持。起點是從基本的支持NetUI(Beehive中包含Page Flow的組件)的項目開始。根據指導構建基本的支持NetUI的Web應用程序。在本文中,我們暫且稱之為“jsf-beehive”,可以在 http://localhost:8080/jsf-beehive 上獲得。 接下來,安裝并配置JSF。Page Flow可以使用任何與JavaServer Faces 1.1兼容的實現,并針對兩種主流實現進行了測試:Apache MyFaces和JSF Reference Implementation。根據下面的指導在新的Web應用程序中安裝JSF:MyFaces v1.0.9及更高版本,JSF Reference Implementation v1.1_01,或者其他實現。之后,可以使用WEB-INF/faces-config.xml中的一個簡單入口啟動Page Flow集成,入口在<application>標簽之下,<navigation-rule>標簽之上: <factory> <application-factory> org.apache.beehive.netui.pageflow.faces.PageFlowApplicationFactory </application-factory> </factory> 添加了這些就為頁面流提供了一個機會,使其可以提供自己的JSF框架對象版本來定制其行為。通常來說,只有在使用頁面流功能的時候,JSF行為才會被修改;JSF的基本行為不會改變。 基本集成JSF中頁面流的最基本用處是引發(調用)來自JSF頁面的動作。JSF頁面可以處理頁面內事件,而頁面流動作則是從一個頁面導航到另一頁面的方法。首先,在Web應用程序中創建一個名為“example”的目錄,在其中創建一個頁面流控制器類: package example; import org.apache.beehive.netui.pageflow.Forward; import org.apache.beehive.netui.pageflow.PageFlowController; import org.apache.beehive.netui.pageflow.annotations.Jpf; @Jpf.Controller( simpleActions={ @Jpf.SimpleAction(name="begin", path="page1.faces") } ) public class ExampleController extends PageFlowController { @Jpf.Action( forwards={ @Jpf.Forward(name="success", path="page2.faces") } ) public Forward goPage2() { Forward fwd = new Forward("success"); return fwd; } } 在這個頁面流中有兩個動作:跳轉到page1.faces的begin動作和跳轉到page2.faces的goPage2動作。將goPage2作為一個方法動作(而不是簡單動作)的原因是稍后將會對其進行擴充。 在構造頁面的時候,應當以.jsp為擴展名創建page1和page2;JSF servlet處理每個.faces請求,并最終跳轉到相關的JSP。所以,跳轉到page1.faces最終將顯示page1.jsp,如下: <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> ? <html> <body> <f:view> <h:form> <h:panelGrid> <h:outputText value="Page 1 of page flow #{pageFlow.URI}"/> <h:commandLink action="goPage2" value="Go to page 2"/> </h:panelGrid> </h:form> </f:view> </body> </html> 從JSF頁面引發一個動作很簡單:使用命令組件的action屬性中的動作名字就可以了。在上面的例子中,commandLink指向goPage2動作。使用頁面流集成,這意味著goPage2動作會在example.ExampleController中運行。 就是這樣。要試驗的話,構建應用程序,點擊 http://localhost:8080/jsf-beehive/example/ExampleController.jpf ,這將通過begin動作跳轉到page1.faces。單擊鏈接“Go to page 2”,會引發goPage2動作并跳轉到page2.faces。 后臺BeanPage Flow框架可以管理與JSF頁面相關的后臺bean(backing bean)。該類是放置與頁面相關的事件處理程序和狀態的方便場所。可以把它看作是集中放置與頁面交互時所運行的所有代碼的單一場所。當點擊一個JSF頁面時,Page Flow會判斷是否有具有同樣名稱和包的類,例如,page /example/page1.faces的example.page1類。如果存在這樣的類,并且它用@Jpf.FacesBacking進行注釋并擴展了FacesBackingBean,它就會創建該類的一個實例。當離開JSF頁面而轉到一個動作或者其它任何頁面時,后臺bean會被銷毀。后臺bean與JSF頁面共存亡。 綁定到后臺bean中的屬性下面是page1.faces的一個非常簡單的后臺bean,以及屬性someProperty。文件名是page1.java: package example; import org.apache.beehive.netui.pageflow.FacesBackingBean; import org.apache.beehive.netui.pageflow.annotations.Jpf; @Jpf.FacesBacking public class page1 extends FacesBackingBean { private String _someProperty = "This is a property value from" + getClass().getName() + "."; public String getSomeProperty() { return _someProperty; } public void setSomeProperty(String someProperty) { _someProperty = someProperty; } } 在JSF頁面(page1.jsp)中,可以利用backing綁定上下文來綁定到這個屬性: <h:outputText value="#{backing.someProperty}"/> 上面的例子顯示了someProperty(最終在后臺bean上調用getSomeProperty())的值。類似地,設置這個值: <h:inputText value="#{backing.someProperty}"/> 注意,在這個例子中,后臺bean中沒有出現事件處理程序或組件引用。這就縮短了代碼;后臺bean是放置頁面所有的處理程序和組件引用的好地方。 從后臺bean引發頁面流動作在上面的“基本集成”部分,我們直接從JSF組件引發頁面流動作。通常情況下,只需這樣即可;當單擊一個按鈕或者鏈接時,會運行一個動作并跳轉到另一個頁面上。如果想在調用控制器之前運行一些與頁面相關的代碼,或者如果希望頁面可以在幾個動作之間進行動態選擇的話,可以在命令處理程序(JSF頁面所運行的一個Java方法)中引發一個動作。下面是一個命令處理程序的例子,可以把它放到后臺bean page2.java中(或者其它任何可公開訪問的bean中): public String chooseNextPage() { return "goPage3"; } 這是一個非常簡單的命令處理程序,它選擇了goPage3動作。可以用標準的JSF方式從一個JSF命令組件綁定到這個命令處理程序: <h:commandButton action="#{backing.chooseNextPage}" value="Submit"/> 當單擊鏈接時,會運行chooseNextPage命令處理程序,它會選擇引發goPage3動作。還可以對命令處理程序方法使用一個特殊的頁面流注釋——@Jpf.CommandHandler: @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3") } ) public String chooseNextPage() { return "goPage3"; } 該注釋使支持Beehive的工具可以知道命令處理程序引發了后臺bean中的哪個動作,并允許擴展JSF動作處理的能力(參見下面“從JSF頁面向頁面流發送數據”部分)。 從后臺bean訪問當前頁面流或共享流在某些情況下,您或許想直接從后臺bean訪問當前頁面流或一個活動的共享流。為此,只需創建一個適當類型的字段,并使用@Jpf.PageFlowField或@Jpf.SharedFlowField對其進行適當注釋: @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3") } ) public String chooseNextPage() { return "goPage3"; } 這些字段將在創建后臺bean的時候被初始化。無需手動對其進行初始化。下面的例子使用了自動初始化的ExampleController字段。在這個例子中,“show hints”單選鈕的事件處理程序在頁面流中設置了一個普通優先級。 @Jpf.PageFlowField private ExampleController myController; @Jpf.SharedFlowField(name="sharedFlow2") // "sharedFlow2" is a // name defined in the // page flow controller private ExampleSharedFlow mySharedFlow; 在很多情況下,頁面不需要直接與頁面流或者共享流進行交互;使用其它方法從頁面流向JSF頁面傳遞數據就足夠了,反之亦然。下面我將給出一些例子。 從頁面流控制器訪問后臺bean您不能從頁面流控制器訪問后臺bean!至少,這不容易做到,這是有意為之的。后臺bean與JSF頁面緊密相關,當您離開頁面的時候,后臺bean會被銷毀。正如頁面流控制器不應了解頁面細節一樣,它也不應了解后臺bean。當然了,可以從后臺bean向控制器傳遞數據(稍后將會介紹),甚至可以傳遞后臺bean實例本身,但是在大多數情況下,后臺bean的內容是不應當泄露給控制器的。 生命周期方法通常,當后臺bean發生某些事情的時候,比如當它被創建或銷毀時,我們希望能運行代碼。在Page Flow框架的生命周期中,它會對后臺bean調用一些方法:
不管要在哪個時期運行代碼,只需重寫適當的方法: protected void onCreate() { /*some create-time logic */ } 當重寫這些方法時,不需要調用空的super版本。 在JSF頁面和頁面流之間傳遞數據現在我們該看看如何在JSF頁面和頁面流之間傳遞數據了。 從頁面流向JSF頁面發送數據通常,您會想要利用頁面流的數據來初始化一個頁面。為此,可以向page2.faces的Forward添加“action outputs”: @Jpf.Action( forwards={ @Jpf.Forward( name="success", path="page2.faces", actionOutputs={ @Jpf.ActionOutput(name="message", type=String.class,required=true) } ) } ) public Forward goPage2() { Forward fwd = new Forward("success"); fwd.addActionOutput("message", "Got the message."); return fwd; } 做完這些之后,可以直接從JSF頁面或者后臺bean將該值作為頁面輸入來訪問。(如果您不喜歡鍵入冗長的注釋,可以省去斜體的。它們主要用于再次檢查添加的對象類型是否正確,確定不缺失類型。) 可以在頁面中利用JSF表示語言中的頁面流pageInput綁定上下文綁定到這個值: <h:outputText value="#{pageInput.message}"/> 注意,可以利用pageFlow和sharedFlow綁定上下文綁定到頁面流控制器自身或者任何可用的共享流的屬性: <h:outputText value="#{pageFlow.someProperty}"/> <h:outputText value="#{sharedFlow.mySharedFlow.someProperty}"/> 最后,要想從后臺bean訪問頁面輸入,只需在bean類代碼中的任意地方調用getPageInput: String message = (String) getPageInput("message"); 從JSF頁面向頁面流發送數據還可以隨著頁面流所引發的動作發送數據。很多動作將要求表單bean作為輸入;通常,表單bean用于從頁面獲取數據送到控制器。首先,讓我們構建一個動作來接收表單bean并跳轉到頁面: @Jpf.Action( forwards={ @Jpf.Forward(name="success", path="page3.faces") } ) public Forward goPage3(NameBean nameBean) { _userName = nameBean.getFirstName() + ' ' + nameBean.getLastName(); return new Forward("success"); } 該動作包含一個NameBean,它是一個將getters/setters作為其firstName和lastName屬性的表單bean類。它設置一個成員變量保存完整名字,之后跳轉到page3.faces。我們知道,可以直接從JSF頁面或者它的后臺bean引發一個動作。在這兩種情況下,都可以向動作發送表單bean。下面讓我們依次看看每種情況。 從后臺bean發送表單bean要從后臺bean中的命令處理程序發送表單bean,需要使用一個特定的注釋。下面給出了page2.java中的情況: private ExampleController.NameBean _nameBean; protected void onCreate() { _nameBean = new ExampleController.NameBean(); } public ExampleController.NameBean getName() { return _nameBean; } @Jpf.CommandHandler( raiseActions={ @Jpf.RaiseAction(action="goPage3", outputFormBean="_nameBean") } ) public String chooseNextPage() { return "goPage3"; } 在這個例子中,JSF頁面可以用它選擇的任何方式填充_nameBean的值(例如,通過將h:inputText值綁定到#{backing.name.firstName}和#{backing.name.lastName})。之后它使用@Jpf.RaiseAction上的outputFormBean屬性來標記_nameBean應當被傳遞到動作goPage3。 從JSF頁面發送表單bean從JSF頁面直接發送表單bean很容易,只要您可以通過數據綁定表達式得到bean值。這是通過在commandButton組件內部添加名為submitFormBean的h:attribute組件來實現的: <h:commandButton action="#{backing.chooseNextPage}" value="Submit directly from page"> <f:attribute name="submitFormBean" value="backing.name" /> </h:commandButton> 在這里,為了使表單bean發送到動作goPage3,按鈕綁定到后臺bean的“name”屬性(getName)。 結束語本文展示了如何將JSF在構建頁面方面的豐富特性與Beehive Page Flow在控制頁面間導航方面的強大功能相結合。二者的集成非常容易,但是卻會對應用造成深遠的影響:它將JSF頁面與應用級邏輯相分離,并把頁面帶入Page Flow所提供的功能領域中。JSF頁面得到了清楚的任務:作為單個(如果有足夠能力的話)視圖元素參與到應用程序的流中。文中沒有展示JSF頁面中具有事件處理功能且控制器中具有復雜的導航邏輯的完備應用程序。但是隨著應用程序的復雜程度提高,它就會更加需要責任的劃分以及頁面流添加給JSF的高級流功能。您可以花幾分鐘嘗試一下——很快您就將意識到這樣做所帶來的好處。 |