首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JSF 請求處理生命週期深度解析:五大階段、CD 管理實戰與即時事件處理

     編輯:Java 相關知識

理解 JSF 請求處理生命週期五大階段,是高效開發、除錯與擴展的關鍵。本文逐階段拆解執行邏輯與事件處理時機,並通過一個完整的 CD 庫存管理 CRUD 應用實戰,示範資料表排序、表單編輯、導航規則、Struts Tiles 整合,以及結合 JavaScript 的即時事件處理技術。

生命週期五階段總覽

  1. 恢復視圖:建立或恢復 viewRoot (組件樹),綁定事件/驗證器。新視圖構建、原始視圖空樹、後視圖恢復狀態三種情況。
  2. 應用請求值:各組件從請求參數 (或 Cookie/Header) 取回提交值。即時組件 (immediate="true") 此階段即完成轉換/驗證並排隊事件;非即時組件僅取值。
  3. 處理驗證:非即時組件進行類型轉換與驗證。失敗則生成訊息、標記無效、跳轉「進行響應」重顯視圖。
  4. 更新模型值:驗證通過後,將組件值推送至 Backing Bean 屬性。
  5. 調用應用:執行 Action 方法 (按鈕/連結綁定),處理業務邏輯,回傳導航結果 (如 success)。
  6. 進行響應:渲染當前視圖 (含錯誤訊息)。可透過 FacesContext.renderResponse() 或 responseComplete() 提前跳轉。

JSF 生命週期圖

圖 1 JSF 生命週期

五階段物件狀態圖

圖 2 JSF 生命週期五階段物件狀態

角色導向的階段關注點

  • 應用開發者:聚焦中間三階段 (應用請求值 → 更新模型值 → 調用應用)。
  • 組件開發者:聚焦首尾兩階段 (恢復視圖、進行響應) —— 編碼/解碼、狀態保存。

實戰應用:CD 庫存管理 (CRUD + 排序 + 即時事件)

技術棧:JSF 1.2、Struts Tiles 佈局、JavaScript 即時提交、Tomcat/JDK 1.5。

核心類別:

  • StoreManagerDelegate:業務代理 (DAO 封裝)。
  • CD:DTO (id, title, artist, price, category, subCategory)。
  • StoreController (別名 CDManagerBean):Backing Bean,Session Scope,連結視圖與模型。

頁面結構

  • listing.jsp:CD 清單 (dataTable),可按標題/藝術家升降序排序,含「Add CD」連結、每行「Edit」連結。
  • cdForm.jsp:新增/編輯表單,含分類單選按鈕 (即時事件) 與子分類下拉框 (動態顯示)。

CD 清單頁面

圖 4 具有可排序列的 CD 清單頁面

未選分類表單

圖 5 尚未選擇分類的 CD 表單頁面

已選分類表單

圖 6 已經選擇了分類和子類組件的 CD 表單頁面

使用案例 1:新增 CD

<!-- listing.jsp -->
<h:commandLink action="#{CDManagerBean.addNew}">
  <f:verbatim>Add CD</f:verbatim>
</h:commandLink>
// StoreController.addNew()
public String addNew() {
    if (subCategoryList == null) subCategoryList = new HtmlSelectOneListbox();
    subCategoryList.setRendered(false);
    this.cd = new CD(); // 重置表單綁定物件
    return "success"; // 導航至 cdForm.jsp
}

faces-config.xml 導航規則:listing.jsp --(addNew/success)--> cdForm.jsp。

cdForm.jsp 關鍵片段

<h:panelGrid columns="3" rowClasses="row1,row2">
  <h:outputLabel for="title"><h:outputText value="Title"/></h:outputLabel>
  <h:inputText id="title" value="#{CDManagerBean.cd.title}" required="true"/>
  <h:message for="title" styleClass="errorText"/>
  ... artist, price 同理 ...

  <h:outputLabel for="category"><h:outputText value="Category"/></h:outputLabel>
  <h:selectOneRadio id="category" value="#{CDManagerBean.cd.category}"
      immediate="true" onclick="submit()"
      valueChangeListener="#{CDManagerBean.categorySelected}">
    <f:selectItems value="#{CDManagerBean.categories}"/>
  </h:selectOneRadio>
  <h:message for="category"/>

  <h:outputLabel for="subcategory"><h:outputText value="Subcategory"/></h:outputLabel>
  <h:selectOneListbox id="subcategory" value="#{CDManagerBean.cd.subCategory}"
      binding="#{CDManagerBean.subCategoryList}">
    <f:selectItems value="#{CDManagerBean.subCategories}"/>
  </h:selectOneListbox>
  <h:message for="subcategory"/>
</h:panelGrid>

<h:commandButton id="submitAdd" action="#{CDManagerBean.addCD}" value="Add CD"
    rendered="#{not CDManagerBean.editMode}"/>
<h:commandButton id="submitUpdate" action="#{CDManagerBean.updateCD}" value="Update CD"
    rendered="#{CDManagerBean.editMode}"/>

使用案例 2:編輯 CD (含 DataModel 排序)

// StoreController - DataModel 封裝 List
private DataModel cdModel = new ListDataModel();
{ cdModel.setWrappedData(store.findTitleAsc()); }
public DataModel getCds() { return cdModel; }
<!-- listing.jsp dataTable -->
<h:dataTable id="items" value="#{CDManagerBean.cds}" var="cd"
    rowClasses="oddRow,evenRow" headerClass="tableHeader">
  <h:column>
    <f:facet name="header">
      <h:panelGroup>
        <h:outputText value="Title"/>
        <h:commandLink action="#{CDManagerBean.sortTitleAsc}"><h:outputText value="asc"/></h:commandLink>,
        <h:commandLink action="#{CDManagerBean.sortTitleDec}"><h:outputText value="dec"/></h:commandLink>
      </h:panelGroup>
    </f:facet>
    <h:commandLink action="#{CDManagerBean.editCD}"><h:outputText value="#{cd.title}"/></h:commandLink>
  </h:column>
  ... artist, price 欄位 ...
</h:dataTable>
// 編輯動作:取得當前行資料,載入完整物件,啟用子分類列表
public String editCD() {
    this.cd = (CD) cdModel.getRowData(); // DataModel 直接取得選中行物件
    this.cd = store.getCDById(cd.getId());
    if (cd.getCategory() != null && !"".equals(cd.getCategory())) {
        subCategoryList.setRendered(true);
        subCategories = getSubcategoriesList(cd.getCategory());
    } else subCategoryList.setRendered(false);
    editMode = true;
    return "success"; // 導航至 cdForm.jsp (同頁面,editMode=true 顯示 Update 按鈕)
}

使用案例 3:即時事件處理 (分類選擇 → 動態載入子分類)

核心機制:immediate="true" 使組件在「應用請求值」階段即完成轉換/驗證並觸發 ValueChangeListener,跳過後續階段直接進入「進行響應」 (FacesContext.renderResponse()),避免整個表單驗證。

<h:selectOneRadio id="category" value="#{CDManagerBean.cd.category}"
    immediate="true" onclick="submit()"
    valueChangeListener="#{CDManagerBean.categorySelected}">
  <f:selectItems value="#{CDManagerBean.categories}"/>
</h:selectOneRadio>
// 事件處理器
public void categorySelected(ValueChangeEvent event) {
    subCategoryList.setRendered(true);
    String newCat = (String) event.getNewValue();
    if (newCat != null) subCategories = getSubcategoriesList(newCat);
    FacesContext.getCurrentInstance().renderResponse(); // 強制跳轉渲染階段
}

組件綁定:binding="#{CDManagerBean.subCategoryList}" 將 HtmlSelectOneListbox 實例注入 Backing Bean,以便程式化控制 setRendered()。

排序功能:無導航規則的邏輯結果

public String sortTitleAsc() {
    cdModel.setWrappedData(store.findTitleAsc());
    return "asc"; // faces-config.xml 無映射 → 重新載入當前視圖
}
public String sortTitleDec() {
    cdModel.setWrappedData(store.findTitleDec());
    return "dec";
}

回傳未映射的邏輯結果 (asc/dec) 導致視圖重渲染,資料表即時反映排序後資料。

Struts Tiles 整合

雖非 JSF 必需,Tiles 可為所有 JSF 頁面提供一致外觀 (Header/Footer/Menu)。cdForm.jsp、listing.jsp 皆作為 Tiles 定義的 body 區塊嵌入。

結語

本文實戰演示了生命週期五階段在真實 CRUD 應用中的協同:表單提交走完整週期,即時事件縮短週期,排序操作利用未映射結果重渲染視圖。DataModel 簡化行選取,binding 實現組件程式化控制,panelGrid + CSS 實現彈性佈局。下一篇將深入類型轉換、驗證器開發與進階組件特性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved