JavaServer Faces (JSF) 是一種伺服器端組件框架,提供基於組件的 Web 使用者介面開發模式。JSF 1.2 整合於 Java EE 5,修正舊版缺失並新增優異特性。本教程系列以實例為主、理論為輔,助您快速上手。第一部分將從零開始,逐步建立一個計算機應用,涵蓋專案結構、web.xml/faces-config.xml 設定、托管 Bean、JSP 視圖組件樹、基本驗證、CSS 美化、國際化訊息、控制器模式與導航機制。
典型 JSF 應用包含:狀態管理的 JavaBean、有狀態 GUI 組件、事件監聽器、代表 MVC View 的 JSP 頁面 (透過組件樹引用 View Root)。
JSF 視圖由 View Root (組件樹狀態) 與 JSP 頁面 (組件宣告) 組成。修改 JSP 屬性重載頁面不會改變組件狀態,因為標籤只尋找現有組件。控制器程式碼修改組件狀態 (如停用欄位),渲染時才反映最新狀態。這使 JSF 更接近傳統 GUI 開發,而非無狀態的 Model 2 架構 (Struts 等)。
JSF 的 MVC 實現中,托管 Bean 扮演協調角色,建議分為「感知 JSF 的控制器 Bean」與「不感知 JSF 的模型 Bean」,業務邏輯委託給模型層,利於測試與重用。
src/main/java - Java 程式碼
src/main/webapp/pages - JSP 頁面
src/main/webapp/WEB-INF - web.xml, faces-config.xml
src/main/webapp/css - 樣式表
src/main/resources - 訊息資源包
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
所有 /faces/* 或 *.jsf 請求將交由 Faces Servlet 處理,初始化 FacesContext 與 ViewRoot。存取 URL 範例:https://localhost:8080/app/faces/pages/calculator.jsp。
<managed-bean>
<managed-bean-name>calculator</managed-bean-name>
<managed-bean-class>com.arcmind.jsfquickstart.model.Calculator</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
request scope 適合無需跨頁面保持狀態的 Bean。JSF 找不到 Bean 時會自動實例化 (需無參建構子)。
public class Calculator {
private int firstNumber = 0, secondNumber = 0, result = 0;
public void add() { result = firstNumber + secondNumber; }
public void multiply() { result = firstNumber * secondNumber; }
public void clear() { result = 0; }
// getter/setter 略
}
純 POJO,完全不依賴 JSF,利於單元測試。
關鍵標籤庫宣告:
<%@ taglib uri="https://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="https://java.sun.com/jsf/core" prefix="f" %>
html 標籤庫處理表單元素,core 處理邏輯/驗證/轉換。
頁面結構:
<f:view>
<h:form id="calcForm">
<h:panelGrid columns="3">
<h:outputLabel value="First Number" for="firstNumber"/>
<h:inputText id="firstNumber" value="#{calculator.firstNumber}" required="true"/>
<h:message for="firstNumber"/>
<h:outputLabel value="Second Number" for="secondNumber"/>
<h:inputText id="secondNumber" value="#{calculator.secondNumber}" required="true"/>
<h:message for="secondNumber"/>
</h:panelGrid>
<h:commandButton action="#{calculator.add}" value="Add"/>
<h:commandButton action="#{calculator.multiply}" value="Multiply"/>
<h:commandButton action="#{calculator.clear}" value="Clear" immediate="true"/>
</h:form>
<h:panelGroup rendered="#{calculator.result != 0}">
<h:outputText value="Result #{calculator.result}"/>
</h:panelGroup>
</f:view>
<f:view> 宣告 JSF 管理區域#{calculator.firstNumber} 雙向同步模型屬性required="true" + 整數屬性 → 自動驗證與轉換錯誤處理immediate="true" 跳過驗證直接執行動作 (Clear 按鈕適用)rendered 條件式顯示結果區塊<h:panelGrid> 取代 HTML Table:rowClasses="oddRow,evenRow" 交替列色,styleClass 套用 CSS 邊框<link rel="stylesheet" href="<%=request.getContextPath()%>/css/main.css"/>label 屬性讓訊息顯示友善名稱requiredMessage="required" converterMessage="not a valid number" 客製化 (JSF 1.2 新增)faces-config.xml 設定 <message-bundle>messages</message-bundle>,messages.properties 覆寫預設鍵值為避免模型耦合 JSF,建立 CalculatorController (Request Scope) 注入 Calculator (None Scope):
<managed-bean>
<managed-bean-name>calculatorController</managed-bean-name>
<managed-bean-class>...CalculatorController</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>calculator</property-name>
<value>#{calculator}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>calculator</managed-bean-name>
<managed-bean-class>...Calculator</managed-bean-class>
<managed-bean-scope>none</managed-bean-scope>
</managed-bean>
組件綁定:JSP 使用 binding="#{calculatorController.resultsPanel}" 將 UIPanel 注入控制器,動態控制 setRendered(true/false) 顯示/隱藏結果區塊,無需視圖邏輯。
訊息處理:FacesContext.getCurrentInstance().addMessage(...) 加入 FacesMessage,JSP 以 <h:messages globalOnly="true"/> 顯示非欄位綁定的全域訊息。
動態樣式:styleClass="#{calculatorController.firstNumberStyleClass}" 依輸入有效性切換標籤顏色 (紅/黑)。
例外恢復:除法分母為零時,捕獲 ArithmeticException,將 secondNumberInput 設為 1 並顯示錯誤訊息,展現組件狀態可程式化操作的強大。
faces-config.xml 定義導航規則:
<navigation-rule>
<from-view-id>/pages/calculator.jsp</from-view-id>
<navigation-case>
<from-outcome>results</from-outcome>
<to-view-id>/pages/results.jsp</to-view-id>
</navigation-case>
</navigation-rule>
"results" → 導航至 results.jsp<redirect/> 元素可讓瀏覽器 URL 同步更新 (解決書籤問題)<h:commandLink action="CALCULATOR_REDIRECT"/> 或 <h:outputLink value="pages/calculator.jsf"> 從首頁進入/pages/* 萬用字元規則JSF 以組件模型讓 Web 開發更接近傳統 GUI,程式碼更少、更易維護。JSF 2.0 將整合 Facelets 概念、原生 Ajax 支援、簡化組件開發。雖面臨 Tapestry 5、Wicket、Struts 2、Spring MVC、GWT、Flex 等競爭,但作為 Java EE 標準擁有活躍社群,前景看好。第二部分將深入生命週期、自訂轉換/驗證、階段監聽器等進階主題。