承接第一部分的基礎應用建立,本教程深入 JSF 1.2 核心進階特性:請求處理生命週期六階段、自訂資料轉換器、多層次驗證機制、階段監聽器 實作物件級驗證框架。透過持續擴充的「聯絡人管理 CRUD 系統」為例,示範如何善用 JSF 組件模型簡化開發。
單視圖 contacts.jsp 承載所有操作,後端包含:
ContactController (Request Scope) - JSF 控制器,管理表單狀態與動作Contact - 領域模型 (含 firstName, lastName, group, tags, type 等屬性)ContactRepository (Application Scope) - 資料存取介面 (CRUD)faces-config.xml - 托管 Bean 宣告與依賴注入 (Repository 注入 Controller)關鍵技術點:UIForm、UICommand 組件綁定控制表單顯示/隱藏與按鈕標籤切換;<h:dataTable> 以 var 映射列表項,搭配 <f:setPropertyActionListener> 於點擊編輯/刪除時將當前行物件注入 selectedContact。
<f:subview> 與 <jsp:include>將表單與列表拆分為 form.jsp、listing.jsp,於主頁面包含,結構更清晰。JSF 1.2 中 <f:subview> 為可選,但部分 IDE 仍要求保留。
| 組件 | 後端渲染器 | 適用場景 |
|---|---|---|
<h:selectOneMenu> | HtmlSelectOneMenu | 單選下拉選單 (如 Group) |
<h:selectOneRadio> | HtmlSelectOneRadio | 單選按鈕群 (如 ContactType Enum) |
<h:selectManyCheckbox> | HtmlSelectManyCheckbox | 多選核取方塊 (如 Tags) |
<h:selectManyListbox> | HtmlSelectManyListbox | 多選清單框 |
<h:selectManyMenu> | HtmlSelectManyMenu | 多選下拉選單 |
<h:selectBooleanCheckbox> | HtmlSelectBooleanCheckbox | 布林值切換 (如 Active) |
<h:inputTextarea> | HtmlInputTextarea | 多行文字輸入 (如 Description) |
Enum 直接綁定:JSF 1.2 內建 Enum 轉換器,<h:selectOneRadio value="#{contactController.contact.type}"> 可直接綁定 ContactType Enum 屬性,無需額外轉換器。
理解生命周期是掌握轉換/驗證時機的關鍵:
FacesServlet 擷取 View ID,建立/恢復組件樹至 FacesContext.viewRoot。新視圖建立樹、初始視圖直接進入階段 6、Postback 還原狀態submittedValue。若 immediate="true" 此階段即轉換/驗證submittedValue 為正確類型 → 執行驗證規則。失敗則加入錯誤訊息至 FacesContext,跳過後續階段直接進入階段 6persist(), read()),導航規則解析下一個視圖開發者可呼叫 FacesContext.renderResponse() 直接跳至階段 6,或 responseComplete() 完全脫離 JSF 流程。
原始型別、包裝類別、String、Enum、BigDecimal/Integer/Double/Float/Boolean/Byte/Character/DateTime 皆有對應轉換器 (如 javax.faces.Integer → IntegerConverter)。
日期/數字格式需顯式指定:<f:convertDateTime pattern="MM/yyyy"/>、<f:convertNumber>。
javax.faces.convert.Converter 介面getAsObject(FacesContext, UIComponent, String):字串 → 物件 (如 ID → Group 物件)getAsString(FacesContext, UIComponent, Object):物件 → 字串 (如 Group → ID)faces-config.xml 註冊 <converter>,指定 converter-for-class 為目標類別範例:GroupConverter 從 Application Map 取得 GroupRepository 查詢物件,TagConverter 同理。註冊後,EL 綁定 #{contactController.contact.group} 即自動套用,無需 JSP 指定。
泛型限制:JSF 不支援 List<Tag> 直接轉換,解法為於 Model 提供 Tag[] getTags() / setTags(Tag[]) 陣列存取器。
<f:validateLongRange minimum="0" maximum="150"/> (年齡)<f:validateLength minimum="2" maximum="25"/> (姓名長度)<f:validateDoubleRange>於動作方法 (persist()) 中呼叫 Model validate(),拋出自訂異常轉為 FacesMessage,回傳 null 留在當前視圖。優點:關注點分離、不需額外類別;缺點:執行時機最晚 (階段 5)。
托管 Bean 定義 validatePhone(FacesContext, UIComponent, Object),於 JSP 以 validator="#{contactValidators.validatePhone}" 綁定。適合應用開發者,無需建立獨立類別。
javax.faces.validator.Validatorvalidate(FacesContext, UIComponent, Object) 拋出 ValidatorExceptionfaces-config.xml 註冊 <validator-id> 與 <validator-class><f:validator validatorId="..."/>範例:ZipCodeValidator 使用 Regex 驗證郵遞區號格式。
生命週期中 轉換先於驗證。若使用自訂 PhoneConverter 將字串轉為 PhoneNumber 物件並內建格式驗證,後續驗證器 (如 ContactValidators.validatePhone) 接收的即為 PhoneNumber 物件,可直接檢查領域邏輯 (如區碼必須為亞利桑那州 520/602)。此模式將格式驗證前置至轉換階段,業務驗證留至驗證階段,分工明確。
PhaseListener 介面public class DebugPhaseListener implements PhaseListener {
public void beforePhase(PhaseEvent e) {
System.out.println("------ BEFORE PHASE " + e.getPhaseId());
}
public void afterPhase(PhaseEvent e) {
System.out.println("------ AFTER PHASE " + e.getPhaseId());
if (e.getPhaseId() == PhaseId.RENDER_RESPONSE)
System.out.println("REQUEST END\n");
}
public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; }
}
於 faces-config.xml 註冊 <lifecycle><phase-listener> 或 JSF 1.2 新增的視圖級綁定 <f:phaseListener binding="#{controller.phaseListener}"/>。
addNew()persist()Validateable 介面含 validate() throws ValidationExceptionContact) 實作介面,封裝業務驗證邏輯 (如至少一組電話)AbstractCrudController 內含匿名 PhaseListener,監聽 UPDATE_MODEL_VALUES 階段結束後呼叫 validate()FacesContext.renderResponse() 跳過 INVOKE_APPLICATION,並加入錯誤訊息ContactController 實作 getFormObject() 依編輯模式回傳驗證目標<f:phaseListener binding="#{contactController.phaseListener}"/>此模式將驗證邏輯下沉至領域模型,控制器僅協調流程,符合關注點分離原則,並可跨專案重用。
JSF 提供彈性、強大且可插拔的框架。標準轉換/驗證滿足快速原型,自訂機制支撐生產級複雜需求。注意轉換器輸出物件類型需與驗證器預期輸入一致。進階組件庫可參考 Tomahawk、Crank、Shale、jsf-comp。JSF 生命週期為資料完整性提供一致可靠的基礎設施,助力從原型到生產的平滑演進。