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

整合 Struts、Tiles 與 JavaServer Faces:打造強健的 J2EE Web 應用架構

     編輯:Java 相關知識

前言:三大技術融合的價值

在 J2EE Web 開發領域,Struts 作為 MVC 框架的事實標準,提供了穩健的控制層;Tiles 強化了頁面組合與版面復用能力;而 JavaServer Faces (JSF) 則帶來了豐富的元件模型、事件驅動機制與渲染器無關的 UI 呈現方式。本文將詳細示範如何透過 Struts-Faces 整合庫,並客製化關鍵類別,將這三者無縫整合,構建兼具彈性、可維護性與豐富前端體驗的企業級應用。

技術背景與版本需求

  • Struts 1.1(含 Tiles)
  • JavaServer Faces Reference Implementation (JSF-RI) Early Access Release 4.0
  • Struts-Faces 整合庫 0.4

注意:當時這三者無法直接互通,需透過客製化類別橋接。

JSF 核心觀念速覽

元件架構:UIComponent、Renderer 與標籤

JSF 將「呈現邏輯」(What)與「商業邏輯」(Why/How)分離。每個 UI 元件(UIComponent)可搭配不同的渲染器(Renderer)輸出 HTML、WML 等不同格式。自訂標籤(繼承 UIComponentTag)負責將元件與渲染器關聯。

JSF 生命週期六大階段

  1. 重建元件樹
  2. 套用請求值
  3. 處理驗證
  4. 更新模型值
  5. 呼叫應用程式
  6. 呈現回應

FacesServlet 作為控制器,委派 Lifecycle 實例依序執行各階段。驗證失敗會直接跳至「呈現回應」階段顯示錯誤。

Managed Bean 機制

透過 faces-config.xml 宣告 JavaBean(如 FlightSearchBean),容器自動實例化並置於指定範圍,免去 JSP 中 jsp:useBean 的撰寫。

Struts-Faces 整合庫核心組件

Struts-Faces 由 Craig McClanahan 建立,主要組件包括:

  • FacesRequestProcessor:繼承 Struts RequestProcessor,專責處理 Faces 請求。
  • ActionListenerImpl:取代 JSF 預設 ActionListener,將 ActionEvent 轉交給 FacesRequestProcessor。
  • FormComponent / FormRenderer / FormTag:支援 Struts 風格的表單處理。
  • LifecycleListener (ServletContextListener):啟動時註冊對應的 RequestProcessor。
  • faces-config.xml:內建於 struts-faces.jar,定義元件與渲染器對應。

整合 Tiles 的五大挑戰與解決方案

Struts-Faces 0.4 尚未原生支援 Tiles,直接搭配會遇到以下問題,需客製化類別解決:

挑戰 1:Response Already Committed(回應已提交)

原因:JSF-RI 的 ViewHandlerImpl 在轉發請求前未檢查回應狀態。Tiles 內部會 include 多個 JSP,導致首次轉發後回應已提交,後續再轉發即拋出錯誤。

解決:實作自訂 STFViewHandlerImpl,覆寫 renderView(),檢查 response.isCommitted(),若已提交則 include,否則 forward。

if (!response.isCommitted()) {
    rd.forward(request, response);
} else {
    rd.include(request, response);
}

並透過 ServletContextListener 在 FacesServlet 初始化前,註冊帶有自訂 ViewHandler 的新 Lifecycle(ID 為 STFLifecycle),在 web.xml 設定 javax.faces.lifecycle.LIFECYCLE_ID 參數。

挑戰 2:404 Resource Not Found(Tiles 定義名無法解析)

原因:FacesRequestProcessor 繼承自 RequestProcessor 而非 TilesRequestProcessor,無法處理 Tiles 定義名(如 /foobar.flight-search)的轉發。

解決:建立 STFRequestProcessor 繼承 TilesRequestProcessor,複製 FacesRequestProcessor 邏輯,並在 struts-config.xml 指定:

<controller processorClass="foobar.stf.application.STFRequestProcessor" />

挑戰 3:表單送出後返回同一頁面、遺失 Header/Footer、驗證失效

原因:Struts-Faces 預設將表單 action 設為 JSP 頁面名稱(如 FlightSearch.jsp),而非 Struts 的 .do 路徑。導致 FormComponent.createActionForm() 找不到對應的 ActionMapping,進而無法取得 ActionForm、執行驗證與轉發。

解決:客製化三個類別:

  1. STFFormComponent:繼承 FormComponent,覆寫 createActionForm(),使用 RequestUtils.getActionMappingName() 解析帶有 .do 後綴的路徑。新增 action 屬性區別於表單名稱。
  2. STFFormRenderer:繼承 FormRenderer,修改 encodeBegin() 輸出正確的 action URL(含 Context Path 與 .do)。
  3. STFFormTag:繼承 FormTag,覆寫 getComponentType()、getRendererType() 回傳客製化類別名稱。

並在 faces-config.xml 宣告新元件與渲染器。

挑戰 4:Base HREF 導致相對路徑錯誤

原因:Tiles 主版面頁面輸出的 <base href=".../MasterLayout.jsp">,導致後續頁面導航基準錯誤。

解決:客製化 STFBaseRenderer(繼承 BaseRenderer),移除 HttpServletRequest.getServletPath(),僅保留 Context Path。搭配 STFBaseTag 指定新渲染器。

整合完成後的架構異動總覽

類別/檔案用途
STFViewHandlerImpl檢查回應提交狀態,決定 forward 或 include
STFContextListener啟動時註冊自訂 Lifecycle 與 ViewHandler
STFRequestProcessor繼承 TilesRequestProcessor,支援 Tiles 定義轉發
web.xml宣告 Listener、設定 LIFECYCLE_ID、FacesServlet 映射
STFFormComponent、STFFormRenderer、STFFormTag修正表單 action 路徑解析與輸出
STFBaseRenderer、STFBaseTag修正 base href 輸出
faces-config.xml宣告客製化元件與渲染器
struts-faces.tld移除原生 LifecycleListener 宣告,避免重複初始化

結語

透過上述相對精簡的客製化,我們成功將 Struts 的控制層彈性、Tiles 的版面組合優勢、JSF 的豐富元件模型整合為一。此架構不僅保護既有投資,更為 J2EE Web 應用開發提供強大而靈活的基礎設施。隨著 JSF 規範成熟,未來整合將更加原生化,但本文展示的客製化思維與模式(ViewHandler、RequestProcessor、Component/Renderer/Tag 三元組)仍具參考價值。

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