FUD 已經圍繞 J2EE 的 JavaServer Faces (JSF) 技術多時了,我覺得現在該讓它停止了 ,或者至少給出一種公允的說法。關於 JSF 的第一個謠傳是,需要一個 WYSIWYG 拖放工具 來進行 JSF 開發。第二個謠傳是,JSF 不支持諸如 Struts 這樣的 MVC Model 2 框架。最 後一個,也是最致命的謠傳是,JSF 開發就是難。
在這個 4 部分的系列文章中,我將盡量以最實際的方式消除所有這三個謠傳,這種方式 就是教您利用 JSF 進行開發。實際上,如果您認為 JSF 開發很難,可能是您沒有用對,幸 運的是,這很容易改正。本期一開始,我將給出 JSF 的一個結構上的概述和一個實際的例子 ,演示了 MVC 和 JSF 的基礎。但是在開始之前,我將花一點時間來劃清 JSF FUD 與事實真 相。
千萬別相信 FUD!
正如前面提到的,關於 JSF 存在三個謠傳,第一個謠傳是,進行 JSF 開發需要 WYSIWYG 工具。簡直是胡說。就像很多 Swing 開發人員不使用 WYSIWYG 來構建 Swing 應用程序一樣 ,您也不需要用 WYSIWYG 編輯器來構建 JSF 應用程序。事實上,不用 WYSIWYG 工具進行的 JSF 開發比利用諸如 Struts 和 WebWork 這樣的傳統 Model 2 框架進行的開發要容易得多 。本文後面我將詳細解釋具體原因,但是現在您只要記住:JSF 開發比 Struts 要容易得多 ,即使不使用 WYSIWYG 工具!
關於 JSF 的下一個謠傳是,不支持 Model 2 架構。目前來說,這實際上說得有點對。但 事實是,Model 2 是針對建立在 Servlets 之上的 Web 開發的 MVC (Model-View- Controller) 的打了折扣的版本。盡管 Model 2 連接到一個無狀態協議 (HTTP),但是 JSF 支持更加豐富的 MVC 模型(這是傳統 GUI 應用程序更加緊密的近似)。盡管 MVC 的基礎使 得 JSF 框架實現比其他框架更難構建,但是有利的是,實現 JSF 的大量實際工作已經不用 您自己完成了,所以您的淨付出減少了,而您的淨受益就顯著增加了。
關於 JSF 開發最主要、流傳最廣的謠傳是,JSF 開發太難了。我經常從那些閱讀過該技 術的大量資料卻沒有親自體驗過的人那裡聽到這種說法,所以我認為我可以輕易澄清這一點 。事實是,如果您將您對 JSF 的觀點建立在它無可否認的廣泛規范上 —— 以及它的所有生 命周期圖表和圖片 —— 那麼該技術很容易讓您發怵。但是請記住這樣一件事情,規范是針 對工具實現者的,而不是針對應用程序開發人員本身。正如前面提到的,JSF 框架設計成對 應用程序開發人員來說非常容易。
事實上,盡管 JSF 的基於組件的、事件驅動的 GUI 開發模型對於 Java 世界來說還有點 新,但是在別處已經存在很長一段時間了。ASP.net 和 Apple 的 WebObjects 都是類似於 JSF 的架構。Tapestry 是一種開放源代碼的、基於 Java 的 Web 組件框架,它采用的方法 有些不同於 JSF 的方法,但是也建立在 Web GUI 組件模型之上。
就現在來說,對 FUD 的談論也許已經足夠了。消除您對 JSF 的偏見的最好方法是,適當 地鑽研這種技術,我們馬上就來做這件事。但是為了避免這成為您對 JSF 的第一印象,我一 開始將給出一個結構上的概述。
JSF 初學者
像 Swing 和 AWT 一樣,JSF 是一個可以提供一組標准的、可重用的 GUI 組件的開發框 架。JSF 用於構建 Web 應用程序接口。JSF 提供以下開發優勢:
行為與表示的完全分離。
對狀態的組件級控制。
事件容易捆綁到服務器端代碼。
利用熟悉的 UI 組件和 Web 層概念。
提供多個標准化的供應商實現。
典型的 JSF 應用程序包含以下部分:
用於管理應用程序狀態和行為的 JavaBeans 組件。
事件驅動的開發(像傳統 GUI 開發中一樣通過偵聽器)。
呈現 MVC 樣式視圖的頁面;頁面通過 JSF 組件樹引用視圖根(view root)。
盡管使用 JSF 需要跨越一些概念上的障礙,但是這樣做是值得的。JSF 的組件狀態管理 、容易使用用戶輸入驗證、細粒度、基於組件的事件處理和容易擴展的架構,都將大大簡化 Web 開發。在接下來的幾小節中,我將更加詳細地解釋這些特性中最重要的特性。
基於組件的架構
JSF 為標准 HTML 中可用的每個輸入字段提供了組件標簽。您也可以為應用程序的特定目 的,或者為了將多個 HTML 組件組合在一起形成一個復合體 —— 例如一個包含三個下拉菜 單的 Data Picker 組件,而編寫自己的自定義組件。JSF 組件是有狀態的。組件的無狀態是 通過 JSF 框架提供的。JSF 使用組件來生成 HTML 響應。
JSF 的組件集包含一個事件發布模型、一個輕量級的 IOC 容器和很多用於幾乎所有其他 公共 GUI 特性的組件,這些特性包括可插入呈現、服務器端驗證、數據轉換、頁面導航管理 ,等等。作為基於組件的架構,JSF 是相當可配置和可擴展的。大多數 JSF 功能,比如導航 和托管 bean 查看,都可以用可插入的組件替換。這種程度的可插入性給予您在構建 Web 應 用程序 GUI 方面相當大的靈活性,並允許您容易地將其他基於組件的技術融入到 JSF 開發 中。例如,對於托管 bean 查看,您可以用更加全功能的 IOC/AOP Spring 框架來取代 JSF 的內置 IOC 框架。
JSF 和 JSP 技術
JSF 應用程序的用戶界面包含 JSP (JavaServer Pages) 頁面。每個 JSP 頁面包含呈現 GUI 功能的 JSF 組件。可以在 JSP 頁面中使用 JSF 自定義標簽庫來做以下事情:呈現 UI 組件、注冊事件處理器、關聯組件與驗證器、關聯組件與數據轉換器,等等。
這就是說,JSF 並不內在地綁定到 JSP 技術。事實上,JSP 頁面使用的 JSF 標簽只是引 用組件,以便顯示組件。當您第一次修改 JSP 頁面以更改 JSF 組件的屬性,並重新加載該 頁面,看到沒有任何事情發生時,您就會認識到這一點。這是因為標簽以其當前狀態查看組 件。因此,如果組件已經存在,自定義標簽將不會修改它的狀態。組件模型允許控制器代碼 更改組件的狀態(例如,禁用一個文本字段),並且當顯示該視圖時,將會顯示組件樹的當 前狀態。
典型的 JSF 應用程序在 UI 中不需要 Java 代碼,需要很少的 JSTL EL (JSP Standard Tag Library,一種表示語言) 代碼。正如前面提到的,JSF 中有很多用於構建和裝配應用程 序的 IDE 工具,並且 JSF GUI 組件似乎還有一個正在增長的第三方市場。不使用 WYSIWYG 工具也可以進行 JSF 開發。
JSF 和 MVC
JSF 是幾年前學過的在 Java 平台上改進 Web 開發技術的課程的結果。這一趨勢開始於 JSP 技術,這一技術很好,只是很容易在 HTML(和類 HTML)頁面中混合 Java 代碼。下一 次提高是 Model 1 架構,它讓開發人員將大多數後端代碼放入 JavaBeans 組件中,然後用 <jsp:useBean> 標簽將 JavaBeans 組件導入 Web 頁面。這對於簡單的 Web 應用程序 工作得很好,但是許多 Java 開發人員不喜歡 JSP 技術這種與 C++ 特性(比如靜態包含) 的協作。所以引入了 Model 2 架構。
本質上,Model 2 架構是用於 Web 應用程序的 MVC 的打了折扣的版本(請參閱“關於 MVC”)。在 Model 2 架構中,控制器是由 Servlets 來表示的,而顯示則委派給 JSP 頁面 。Struts 是一種簡化的 Model 2 實現,其中的 Actions 代替了 Servlets。在 Struts 中 ,應用程序的控制器邏輯是與它的數據(由 ActionForms 表示)相分離的。對於 Struts 的 主要抱怨是,它感覺上更像過程化的,而不像面向對象的。WebWork 和 Spring MVC 是另外 兩個 Model 2 架構,它們通過更加不像過程化的,在 Struts 的基礎上有所改進,但是它們 仍然沒有 Struts 那樣被廣泛接受(或者沒有那麼成熟,有人可能對此有爭議)。並且也不 提供像 JSF 提供的那些組件模型。
關於大多數 Model 2 框架的實際問題是,事件模型太簡單了(本質上是一個非常縮小的 MVC),這就給開發人員留下了太多的工作。更豐富的事件模型使得創建大多數用戶期望的交 互更加容易。像 JSP 技術一樣,大多數 Model 2 也很容易利用 GUI 自定義標簽來混合 HTML 布局和格式化,這些標簽有點類似於組件。而有些 Model 架構(比如 Struts)出現分 離行為與狀態的錯誤,這讓許多 Java 開發人員感覺自己是在進行 COBOL 編程。
更豐富的 MVC 環境
JSF 提供一個組件模型和一個比大多數 Model 2 實現更豐富的 MVC 環境。本質上,JSF 比 Model 2 架構更加接近於真正的 MVC 編程環境,盡管它仍然是一種無狀態的協議。JSF 也比 Model 2 架構更方便構建更加細致的事件驅動 GUI。盡管 JSF 給了您很多事件選項( 菜單項選擇、按鈕單擊,等等),但是大多數 Model 2 依賴於更加簡單的“請求接受”。
JSF 的良好調優的事件模型,允許您的應用程序與 HTTP 細節的聯系更少,並簡化了開發 。通過使得更加容易將表示和業務邏輯移出控制器,以及將業務邏輯移出 JSP 頁面,JSF 也 在傳統的 Model 2 架構上有了一些改進。事實上,簡單的控制器類根本與 JSF 沒有聯系, 這使得它們更加容易測試。與真正的 MVC 架構不一樣,JSF 模型層不可能發出許多必須在多 個視窗(viewport)中解決的事件;此外,我們仍然在處理無狀態的協議,所以這是沒必要 的。用於更改或更新視圖的系統事件幾乎總是(為什麼我敢說總是呢?)用戶請求。
JSF 的 MVC 實現細節
在 JSF 的 MVC 實現中,mapping backing beans(映射支持 beans)在視圖和模型之間 調停。因此,限制 backing beans 中的業務邏輯和持久性邏輯很重要。一個常見的替代方法 是,將業務邏輯委派給應用程序模型。在這種情況下,backing beans 也映射模型對象,其 中視圖可以顯示它們。另一種選項是,將業務邏輯放在 Business 代表中,後者充當模型。
與 JSP 技術不一樣,JSF 的視圖實現是一個有狀態的組件模型。JSF 視圖包含兩個部分 :視圖根和 JSP 頁面。視圖根是 UI 組件集合,這些組件維護 UI 的狀態。與 Swing 和 AWT 一樣,JSF 組件使用 Composite 設計模式來管理組件樹(簡單地說,容器包含組件,容 器也是一個組件)。JSP 頁面將 UI 組件綁定到 JSP 頁面,並允許您將字段組件綁定到 backing beans 的屬性(或者屬性的屬性),以及將按鈕綁定到事件處理器和操作方法。
下面是一個從 MVC 角度來看的示例應用程序(後面會詳細介紹)。
圖 1. 從 MVC 角度來看的示例應用程序
這已足夠小了:我們來看 JSF!
一個 JSF 例子
對於本文的其余部分,我把重點放在用 JSF 實際創建應用程序的詳細步驟上。該示例應 用程序是 JavaServer Faces 技術的一個非常簡單的演示。演示了以下幾個方面:
如何為部署布局 JSF 應用程序。
如何為 JSF 配置 web.xml 文件。
如何為應用程序配置 faces-config.xml。
編寫 Model beans(也叫做 backing beans)。
使用 JSP 技術構造視圖。
使用自定義標簽庫在視圖根中構造組件樹。
表單字段的默認驗證。
該例是一個簡單的 Calculator 應用程序。創建該應用程序的目標是向終端用戶呈現一個 頁面,讓他/她輸入兩個數值。因此,該頁面具有兩個文本字段、兩個標簽、兩個錯誤消息位 置和一個 Submit 按鈕。文本字段用於輸入數值。標簽用於標注字段。錯誤消息位置用於顯 示針對文本字段的驗證或數據轉換錯誤消息。存在三個 JSP 頁面:index.jsp,它只是重定 向到 calculator.jsp;calculator.jsp,它呈現前面提到的 GUI;results.jsp,它顯示結 果。 一個叫做 CalculatorController 的托管 bean 充當 calculator.jsp 和 results.jsp 的 backing bean。
圖 2 展示了示例 Calculator 應用程序的第二個 MVC 視圖。通過單擊本頁頂部或底部的 Code 圖標,可以下載該應用程序的源代碼。
圖 2. 示例應用程序的第二個 MVC 視圖
構建應用程序
要用 JSF 構建 Calculator 應用程序,需要做以下事情:
收集 web.xml 和 faces-config.xml 文件,建立在示例應用程序的 src/webapp/WEB-INF 目錄下。
在 web.xml 文件中聲明 Faces Servlet 和 Faces Servlet 映射。
在 web.xml 文件中指定 faces-config.xml 文件。
在 faces-config.xml 文件中聲明哪些 beans 由 JSF 托管。
在 faces-config.xml 文件中聲明導航規則。
查看模型對象 Calculator。
使用 CalculatorController 與 Calculator 模型交談。
創建 index.jsp 頁面。
創建 calculator.jsp 頁面。
創建 results.jsp 頁面。
忽略第 1 步,因為這實際上只是設置,我將詳細介紹每一步。
聲明 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>
這非常類似於大多數 web.xml 描述符,只是您將控制權交給 JSF Servlet 來處理請求, 而不是指定自己的 Servlet。對使用 f:view 的 JSP 文件的所有請求都必須經過該 Servlet 。因此,您需要添加一個映射,並且通過該映射只加載支持 JSF 的 JSP 技術,如下所示。
<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/calc/*</url-pattern>
</servlet-mapping>
上面的代碼告訴 Faces Servlet 容器,將映射到 /calc/ 的所有請求發送到 Faces Servlet 進行處理。這允許 JSF 初始化 JSF 上下文和視圖根。
指定 faces-config.xml 文件
如果您將外觀配置文件命名為 faces-config.xml,並放在您的 Web 應用程序的 WEB-INF 目錄中,那麼 Faces Servlet 將自動找到並使用它(因為它是默認的)。另外,您也可以通 過 web.xml 文件中的一個初始化參數 —— javax.faces.application.CONFIG_FILES —— 用一個以逗號分隔的文件列表作為參數,下載一個或多個應用程序配置文件。您可能願意對 除最簡單的之外的所有 JSF Web 應用程序使用第二種方法。
聲明 bean 托管
接下來,您將聲明哪些 beans 由 JSF GUI 組件使用。該示例應用程序只有一個映射 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.CalculatorConroller</managed- bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
上面的配置告訴 JSF,您想要將一個 bean 添加到叫做 CalcBean 的 JSF 上下文。您可 以向自己的托管 bean 調用任何事情。聲明了 beans 之後,下一步是為應用程序指出高級別 的導航規則。
聲明導航規則
對於這個簡單的應用程序,您只需要建立從 calculator.jsp 頁面到 results.jsp 頁面 的導航規則,如下所示。
<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>
上面的導航規則指出,如果一個操作從 /calculator.jsp 視圖返回邏輯結果“success” ,那麼就會將用戶轉向 /results.jsp 視圖。
查看模型對象
由於我的目標是演示如何開始進行 JSF 開發,所以我讓模型對象保持非常簡單。該應用 程序的模型包含在一個模型對象中,如清單 1 所示。
清單 1. Calculator 應用程序的模型對象
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;
}
}
隨即,業務邏輯都設置好了。下一步是將業務邏輯粘接到 Web 應用程序接口中。
粘接模型和視圖
控制器的目標是充當從模型到視圖的粘合劑。Controller 對象的其中一個功能是保持模 型對於視圖技術不可知。正如從下面可以看到的,控制器指定三個 JavaBeans 屬性,這些屬 性將用於收集輸入和顯示結果。這三個屬性是:results(輸出)、firstNumber(輸入)和 secondNumber(輸入)。Controller 也呈現兩個操作,它們委派給 Calculator 對象中相同 名稱的操作。清單 2 展示了 CalculatorController 的代碼。
清單 2. CalculatorController
package com.arcmind.jsfquickstart.controller;
import com.arcmind.jsfquickstart.model.Calculator;
/**
* Calculator Controller
*
* @author $author$
* @version $Revision$
*/
public class CalculatorConroller {
//~ 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 CalculatorConroller object.
*/
public CalculatorConroller() {
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";
}
}
注意,在清單 2 中,multiply 和 add 方法返回“success”。字符串 success 表示一 個邏輯結果。注意它不是關鍵字。您在 faces-config.xml 中指定導航規則時,使用過字符 串 success,因此,在 add(加) 或 multiply(乘)操作執行之後,應用程序將把用戶轉 向到 results.jsp 頁面。
隨即,您就完成了 backing 代碼。接下來指定呈現應用程序視圖的 JSP 頁面和組件樹。
創建 index.jsp 頁面
該應用程序中 index.jsp 頁面的用途是,確保 /calculator.jsp 頁面加載到 JSF 上下 文中,以便該頁面可以找到相應的視圖根。index.jsp 頁面看起來像下面這樣:
<jsp:forward page="/calc/calculator.jsp" />
該頁面所做的所有事情就是將用戶重定向到 “calc” Web 上下文下的 calculator.jsp 。這將 calculator.jsp 頁面置於 JSF 上下文之下,在這裡可以找到它的視圖根。
創建 calculator.jsp 頁面
calculator.jsp 頁面是 Calculator 應用程序的視圖的內容。該頁面接受來自用戶的兩 個數值,如圖 3 所示。
圖 3. Calculator 頁面
因為該頁面很復雜,所以我要向您一步一步地展示如何構建它。一開始是聲明 JSF 標簽 庫( taglib),如下所示:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
上面的代碼告訴 JSP 引擎,您想要使用兩個 JSF 標簽庫,即 html 和 core。html 標簽 庫包含用於處理表單和其他特定於 HTML 的事情的所有標簽。core 標簽庫包含所有的邏輯、 驗證、控制器和其他特定於 JSF 的標簽。
一旦將該頁面布局到普通 HTML 中,您就想要告訴 JSF 系統,您將使用 JSF 來管理組件 。這可以通過使用 <f:view> 標簽來做到,該標簽通知容器,您將使用 JSF 來管理其 中的組件。(這個標簽應該讀作“f 冒號 view”,特別強調這個冒號很重要,以免您出錯! )
沒有 <f:view>,JSF 就無法構建組件樹,並且以後也無法查看已經創建好的組件 樹。像下面這樣使用 <f:view> 標簽:
<f:view>
<h:form id="calcForm">
...
</h:form>
</f:view>
上面的第一行是 <f:view> 的聲明,告訴容器它是由 JSF 托管的。下一行是 <h:form> 標簽,告訴 JSF 您在這裡想要一個 HTML 表單。在呈現階段,包含在表單 組件中的組件將被查看到,並被要求呈現它們自己,因此它們將向輸出產生標准的 HTML。
接下來,您告訴 JSF 您想在該表單中包含其他哪些組件。在 <h:form> 中,您聲 明一個 panelGrid。panelGrid 是一個復合組件 —— 也就是說,一個包含其他組件的組件 。panelGrid 指定其他組件的布局。panelGrid 的聲明如清單 3 中所示。
清單 3. 聲明 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>
屬性 column 被設置為 3,表明組件將被布局到一個具有 3 列的網格中。您添加 6 個組 件到 panelGrid 中,也就是說 2 行。每行包含一個 outputLabel、一個 inputText 和一個 message。標簽和消息被關聯到 inputText 組件,因此,當一個驗證錯誤或錯誤消息被關聯 到 textField 時,該消息就會展示在 message 組件中。這兩個文本字段都是必需的,這意 味著,如果在提交時沒有它們的值,就會創建一條錯誤消息,而控制就會返回到該視圖,即 /calculator.jsp。
注意,兩個 inputFields 都對值屬性使用 JSF EL (JavaServer Faces Expression Language) 值綁定(例如,value="#{CalcBean.firstNumber}")。乍一看,這很像 JSTL EL 。但是 JSF EL 代碼實際上將字段與 backing beans 屬性的相應值相關聯。該關聯是反射性 的,也就是說,如果 firstNumber 是 100,那麼顯示該表單時就會展示出 100。同樣,如果 用戶提交了一個有效值,比如 200,那麼 200 就成了 firstNumber 屬性的新值。
一個更加常見(但也是更棘手)的方法是,用於 backing bean 通過屬性暴露模型對象, 並將這些模型對象屬性綁定到字段。在本系列以後的文章中將會看到該方法的一個例子。
除了字段之外,通過在 panelGroup 中使用兩個 commandButton,calcForm 也與兩個操 作相關聯,如下所示。
<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}" 將按鈕綁定到 backing bean 上的一個方法。因此,當用按鈕提 交表單時,關聯的方法就會被調用(假設所有驗證無誤)。
至此,編寫 JSF 應用程序的最艱難的工作已經完成了。最後兩步將是微不足道的。
創建 results.jsp 頁面
results.jsp 頁面用於顯示最終計算器操作的結果。它的定義如清單 4 中所示。
清單 4. results.jsp 頁面
<%@ 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 文件是一個相對簡單的頁面,用於將加法結果顯示給用戶。它是通過 <outputText> 標簽來做到這一點。<outputText> 標簽有一個 id 和 value 屬 性。value 屬性在呈現時將 bean 值輸出為字符串。value 屬性使用 JSF 將輸出值綁定到 backing bean 屬性(即 firstNumber、secondNumber 和 result)。
運行應用程序!
要運行該應用程序,請轉到 war 文件被映射的頁面。這導致 index.jsp 文件加載 calculator.jsp 頁面。如果您在 firstNumber 字段或 secondNumber 字段輸入一些有效的 文本(例如,“abc”)並提交,那麼您將被帶回 /calculator.jsp 視圖,並且會在相應的 字段邊上顯示一條錯誤消息。如果您讓 firstNumber 字段或 secondNumber 字段保持為空並 提交,那麼您將被帶回 /calculator.jsp 視圖,並且會在相應的字段邊上顯示一條錯誤消息 。因此,您可以看到,在 JSF 中,一些驗證幾乎是自動的,只要指定字段是必需的,並將字 段綁定到 int 屬性即可。
圖 4 展示了應用程序是如何處理驗證和數據轉換錯誤的。
圖 4. 驗證和數據轉換錯誤
結束語
如果在閱讀完這篇對 JSF 的介紹之後,您還有一點持懷疑態度,那麼不要擔心,您已經 越過了最艱難的一道坎了。了解 JSF 的概念框架對於執行該技術來說已經成功了一大半,而 且馬上您就會認識到做這樣的了解是值得的。
以防您還認為用 Struts 編寫應用程序容易一些,我做了一個估計,創建本文的這個簡單 的 JSF 應用程序的 Struts 版本,至少要花費相當於這裡所花費的兩倍的精力。要用 Struts 構建這個相同的示例應用程序,需要兩個操作類用於兩個按鈕,每個類又需要它自己 的一組操作映射。您還需要一個操作映射用於加載第一個頁面,這是在至少假設您遵循 Model 2 推薦的條件下的情況。要模仿 JSF 默認錯誤處理和驗證,還必須配置 Struts 使用 驗證器框架,或者在 ActionForm 上的 validate 方法中實現等價的操作。您必須要麼在 Struts 配中聲明一個 DynaValidatorForm,要麼創建一個 ActionForm 並覆蓋 validate 方 法,要麼使用 ValidatorForm 的子類,並在驗證器框架中放置鉤子。最後,可能需要配置一 些轉向(可能是每個操作兩組)或者一些由所有操作使用的全局轉向。
除了加倍編碼工作之外,Struts 要花費新開發人員更多的精力去學習它。因為我編寫並 講授過 Struts 教程和 JSF 教程,所以我知道這一點。開發人員學習 JSF 容易,而學習 Struts 困難。我相信,JSF 設計中比 Struts 中考慮了更多的長遠因素。JSF 只是更加邏輯 性一些,但是它也是直觀的。Struts 是被收集和演變的。JSF 是被指定和創建的。在我的書 中,JSF 開發只是比 Struts 開發的生產效率更高。
這就結束了 JSF 系列中的第一篇文章。下一篇文章將接著本文進行介紹。我將介紹 JSF 請求處理生命周期的主要階段,並將指出示例應用程序的不同部分適用於生命周期的哪個階 段。我還要介紹即時事件處理的概念,並試圖讓您更完整地了解 JSF 的組件事件模型,包括 討論了與該技術一同發布的許多內置組件。我還要談論一點關於如何將 JavaScript 與 JSF 進行組合,所以請務必關注下一期的文章!