為了將 Struts Web 應(yīng)用程序與 JSF 集成,遵循以下步驟:
- 將 struts-faces.jar 文件與特定于 JSF 的 JAR(jsf-api.jar、jsf-ri.jar) 添加到 Web 應(yīng)用程序的? WEB-INF/lib 目錄中。?
- 如果準備使用 JSF 和 JSTL,則將特定于 JSTL 的 JAR(jstl.jar、standard.jar)添加到? WEB-INF/lib ?文件夾中。這一步只有在部署到常規(guī) Tomcat 時才會需要。JWSDP 已經(jīng)提供了這些 JAR。?
- 修改 Web 應(yīng)用程序部署描述符 (? /WEB-INF/web.xml )以便有一個 Faces Servlet 項, 如清單 5 所示。?
-
修改 JSP 頁面以使用 JSF 和 Struts-Faces 標記而不是 Struts 標記。特別是用 Struts-Faces 相應(yīng)標記替換?
html、b
?ase、
?form
?和?errors
?標記。用 JSF 相應(yīng)標記替換?text
?、?textarea
?和?radio
?標記。Struts-Faces 沒有單獨針對這些的標記。盡管沒有要求,但是您可能還會考慮用 JSTL 標記替換 Struts Logic 標記。? - 對于每一個使用 JSF 標記的 JSP,修改 struts-config.xml 文件以在指向該 JSP 的 Action Mapping 中的? global-forwards 和? local-forwards 中加入前綴? /faces 。?
- 如果 Web 應(yīng)用程序使用了任何您創(chuàng)建的自定義組件,那么您就需要用 JSF 實現(xiàn)的默認 RenderKit 注冊它們。可以通過在? WEB-INF ?文件中創(chuàng)建一個 faces-config.xml 文件、并增加每一個組件和 renderer 的項做到這一點。不過,要記住 faces-config.xml 文件已經(jīng)綁定在 struts-faces.jar 文件中了。您必須從 struts-faces.jar 文件中提出它、加入自己的內(nèi)容并將它放到? WEB-INF 文件夾中。
清單 5. 在 web.xml 中聲明 FacesServlet
?
<servlet >
<servlet-name > faces </servlet-name >
<servlet-class > javax.faces.webapp.FacesServlet </servlet-class >
<load-on-startup > 1 </load-on-startup >
</servlet >
<!-- JavaServer Faces Servlet Mapping -->
<servlet-mapping >
? <servlet-name > faces </servlet-name >
? <url-pattern > /faces/* </url-pattern >
</servlet-mapping >
集成 Struts-Faces 和 Tiles 的挑戰(zhàn)
Struts-Faces 庫提供了 Struts 與 JSF 之間的一個高效的橋梁,使得在 J2EE Web 應(yīng)用程序中擁有豐富的表示層成為現(xiàn)實。您可以通過在組合體中添加 Titles 使表示層更豐富,這樣不僅得到了 Struts 和 JSF 組合的好處,而且還可以高效地重復使用不同的 JSP 頁面,因為它們將由可以根據(jù)需要添加或者刪除的組件部分或者 tiles 所構(gòu)成。
本文已經(jīng)展示了 Struts 和 JSP 的集成,您會想將 Tiles 加入到組合中只是小事一樁,是不是?
不幸的是,JSF 仍然處于早期階段,還沒有給出最后的發(fā)布。基于這一考慮,Struts-Faces 集成軟件開發(fā)仍然在不斷地發(fā)展以包括 JSF 的不同的功能,并且還沒有支持 Tiles。
Struts 和 Tiles 可以無縫地共同工作,但是在集成之路上您會遇到路障。在下面幾小節(jié)中,您會看到在與 Tiles 共同使用 Struts-Faces 集成庫時經(jīng)常遇到的問題的匯總。對于每一個問題,我們詳細說明了一個修改 Struts-Faces 類的解決方案。我們將用一個航班搜索示例解釋這個解決方案。
清單 6 展示了航班搜索頁面的布局。注意我們稱它為航班搜索頁面而不是 FlightSearch.jsp。這是因為 FlightSearch JSP 是用戶在 foobar 旅行 Web 站點看到的合成頁面的主體。
現(xiàn)在,我們保持實際的 FlightSearch.jsp 不變。我們將隨著進展改變它。在您這邊,也需要用航班搜索頁的定義創(chuàng)建一個 Tiles 定義文件。清單 7(緊接著清單 6)展示了 Tiles 定義文件中航班搜索頁的一項。注意對帶有?
extends
?屬性的主布局模板的重復使用。
在清單 6 和 7 后是每一個可能的挑戰(zhàn)。
?
<%@ taglib uri= "http://jakarta.apache.org/struts/tags-faces" prefix= "s" % >
<!-- Layout component parameters: header, menu, body, footer -->
<s :html>
<head>
? <title> <tiles:getAsString name = "title" / > </title>
? <s :base/ >
</head>
<body>
? <TABLE border = "0" width = "100%" cellspacing = "5" >
? ? <tr>
? ? ? <td> <tiles:insert attribute= "header" / > </td>
? ? </tr>
? ? <tr>
? ? ? <td> <tiles:insert attribute= "body" / > </td>
? ? </tr>
? ? <tr> <td> <hr> </td> </tr>
? ? <tr>
? ? ? <td> <tiles:insert attribute= "footer" / > </td>
? ? </tr>
? </TABLE>
</body>
</s :html>
<definition name = "foobar.master-layout"
? path = "/faces/layout/MasterLayout.jsp" >
? ? ? <put name = "title" ? value = "Welcome to Foo Bar Travels" />
? ? ? <put name = "header" value = "/faces/common/header.jsp" />
? ? ? <put name = "footer" value = "/faces/common/footer.jsp" />
? ? ? <put name = "body" ? ? value = "" />
</definition >
? <!-- Definition for Flight Search Page -->
<definition name = "/foobar.flight-search"
? extends = "foobar.master-layout" >
? ? ? <put name = "body" ? ? value = "/faces/FlightSearch.jsp" />
</definition >
這是您在試圖訪問航班搜索表單時馬上會看到的第一個問題。小心查看堆棧跟蹤。您會看到問題出在類?
com.sun.faces.lifecycle.ViewHandlerImpl
?上。這是一個實現(xiàn)了?
ViewHandler
?接口的 JSF-RI 類。
圖 2
展示了?
ViewHandler
?所扮演的角色。這是一個將請求轉(zhuǎn)發(fā)給下一頁的類。在轉(zhuǎn)發(fā)請求時,它不在轉(zhuǎn)發(fā)前檢查響應(yīng)的狀態(tài) -- 這只有在使用 Tiles 時才會發(fā)生,因為 Tiles 內(nèi)部將 JSP 頁面包括在響應(yīng)內(nèi),而 JSF-RI 在第一次轉(zhuǎn)發(fā)后提交響應(yīng)、然后試圖再次轉(zhuǎn)發(fā)給下面的包括 JSP 的 Tiles。
要解決這個問題,必須創(chuàng)建一個自定義的?
ViewHandler
?實現(xiàn),它將檢查響應(yīng)的狀態(tài)以確定它是否提交過。如果響應(yīng)沒有提交過,那么請求就轉(zhuǎn)發(fā)給下一頁,否則,就加入請求并顯示相應(yīng)的 JSP。我們將創(chuàng)建一個名為?
STFViewHandlerImpl
?的類,它實現(xiàn)了?
ViewHandler
?接口并實現(xiàn)了所需要的方法?
renderView()。
?清單 8 展示了?
STFViewHandlerImpl
?中的?
renderView()
?方法:
Tree tree = context. getTree ( ) ;
String requestURI = context. getTree ( ) . getTreeId ( ) ;
rd = request. getRequestDispatcher ( requestURI ) ;
/** If the response is committed, include the resource **/
if ( !response. isCommitted ( ) ) {
? ?rd. forward ( request, context. getServletResponse ( ) ) ;
}
else {
? ?rd. include ( request, context. getServletResponse ( ) ) ;
}
現(xiàn)在您實現(xiàn)了自己的?
ViewHandler
?,如何通知 JSF-RI 使用您的?
ViewHandler
?而不是默認的實現(xiàn)呢?要回答這個問題,就必須理解?
FacesServlet
?的工作過程。
在 Faces 初始化過程中,?
FacesServlet
?會讓?
LifecycleFactory
?實現(xiàn)返回?
Lifecycle
?類的一個實現(xiàn),如清單 9 所示:
清單 9. FacesServlet 中 Faces 的初始化
?
LifecycleFactory factory = ( LifecycleFactory )
? FactoryFinder. getFactory ( "javax.faces.lifecycle.LifecycleFactory" ) ;
//Get the context param from web.xml
String lifecycleID =
getServletContext ( ) . getInitParameter ( "javax.faces.lifecycle.LIFECYCLE_ID" ) ;
//Get the Lifecycle Implementation
Lifecycle lifecycle = factory. getLifecycle ( lifeCycleID ) ;
Lifecycle
?實現(xiàn)對象擁有在呈現(xiàn)響應(yīng)階段要使用的?
ViewHandler
?。您可以通過對?
Lifecycle
?實現(xiàn)調(diào)用?
setViewHandler
?方法讓自己的?
ViewHandler
?實現(xiàn)成為默認的。
現(xiàn)在問題變?yōu)槿绾蔚玫侥J?
Lifecycle
?實現(xiàn)?回答是不需要這樣做。只要創(chuàng)建一個新的實現(xiàn)并用一個惟一 ID 注冊它,如清單 10 所示:
清單 10. 注冊自定義 ViewHandler 和 Lifecycle
?
LifecycleFactory factory = ( LifecycleFactory )
? FactoryFinder. getFactory ( "javax.faces.lifecycle.LifecycleFactory" ) ;
//Create a new instance of Lifecycle implementation -
//com.sun.faces.lifecycle.LifecycleImpl
//According to the documentation, factory.getLifecycle("STFLifecycle")
//should work, but JSF-RI has a defect.
//Hence this workaround of creating a RI class explicitly.
LifecycleImpl stfLifecycleImpl = new LifecycleImpl ( ) ;
//Create a new instance of our STFViewHandler and set it on the Lifecycle
stfLifecycleImpl. setViewHandler ( new STFViewHandlerImpl ( ) ) ;
//Register the new lifecycle with the factory with a unique
//name "STFLifecycle"
factory. addLifecycle ( "STFLifecycle" , stfLifecycleImpl ) ;
lifecycleId
?硬編碼為?
STFLifecycle
?。實際上不是這樣。當您回過頭分析?
清單 9
時就會清楚。?
FacesServlet
?從在 web.xml 文件中聲明的上下文參數(shù)中得到名為?
javax.faces.lifecycle.LIFECYCLE_ID
?的 lifecycle ID,如下所示:
? ? ? ? <param-name > javax.faces.lifecycle.LIFECYCLE_ID </param-name >
? ? ? ? <param-value > STFLifecycle </param-value >
? ? </context-param >
因為?
FacesServlet
?取決于其初始化時的?
Lifecycle
?實現(xiàn),在?
清單 10
中展示的代碼應(yīng)該在?
FacesServlet
?初始化之前執(zhí)行。通過創(chuàng)建另一個 servlet 并在?
FacesServlet
?之前初始化它而做到這一點。
但是一種更聰明的辦法是實現(xiàn)一個?
ServletContextListener
?接口。這個類聲明兩個方法:?
contextInitialized()
?和?
contextDestroyed()
?,在 Web 應(yīng)用程序被創(chuàng)建及 Web 應(yīng)用程序被銷毀之前會分別調(diào)用它們。因而?
清單 10
中的代碼在?
contextInitialized()
?方法中執(zhí)行,而自定義?
ViewHandler
?已經(jīng)用標識名?
STFLifecycle
?注冊到?
Lifecycle
?,并且可被?
FacesServlet
?使用。?
ServletContextListener
?類本身是在 web.xml 文件中聲明的,如下所示:
? <listener-class > foo.bar.stf.application.STFContextListener
? </listener-class >
</listener >
這不是注冊一個帶有自定義?
ViewHandler
?的?
Lifecycle
?惟一方法。事實上?
FactoryFinder
?實現(xiàn)了自己的發(fā)現(xiàn)算法以發(fā)現(xiàn)?
Factory
?對象,包括?
LifecycleFactory
?。這些機制按照順序包括在系統(tǒng)屬性中查看工廠實現(xiàn)類名的機制、faces.properties file、或者 1.3 Services 發(fā)現(xiàn)機制(?
META-INF/services/{factory-class-name}
?)。不過,我們討論的這種機制是最容易的,也是最不具有破壞性的一種。
在解決了提交響應(yīng)的問題后,單擊任何一個 Tiles 特定的鏈接或者輸入一個會呈現(xiàn) Faces 響應(yīng)的 URL。在這里,可以輸入顯示?
FlightSearchForm
?的 URL。
在這樣做了以后,您會得到一個?
foobar.flight-search - 404 Resource Not Found
?錯誤。?
foobar.flight-search
?是航班搜索頁面的 Tiles 定義的名字。?
FacesRequestProcessor
?不能處理 Tiles 請求(因為它擴展的是
RequestProcessor
?而不是?
TilesRequestProcessor
?),所以會得到錯誤。
為解決這個問題,我們將創(chuàng)建一個名為?
STFRequestProcessor
?(表示?
Struts-Tiles-Faces Request Processor
)的新的請求處理程序。現(xiàn)在我們將拷貝?
FacesRequestProcessor
?的所有代碼到這個新類中。惟一的區(qū)別是?
STFRequestProcessor
?繼承的是?
TilesRequestProcessor
?而不是繼承常規(guī)的?
RequestProcessor
?。這個新的?
RequestProcessor
?可以處理 Tiles 請求。清單 11 詳細列出了這個?
STFRequestProcessor
?:
清單 11. STFRequestProcessor.java
正如您所知道的,?
Struts
?框架的?
RequestProcessor
?是在 struts-config.xml 文件中指定的。將下面的項添加到?
struts-cinfig.xml?
文件中后,?
STFRequestProcessor
?就成為處理程序:
<controller processorClass="foobar.stf.application.STFRequestProcessor" />
由于?
STFRequestProcessor
?的作用,這時您就可以瀏覽并查看航班頁面了。不過,在提交航班搜索表單時,您會得到返回來的同一個表單,而且沒有頁頭和頁腳!并且沒有驗證錯誤。事實上,根本就沒有進行驗證!
為了了解到底發(fā)生了什么事情,我們用瀏覽器回到航班頁面并檢查 HTML 源代碼。您會看到像下面這樣的一項:
?
|
?
注意表單 action 是指向 JSP 頁而不是一個? .do ?的。啊哈!這就是問題!這不是由于同時使用 Tiles 和 Struts-Faces 而帶來的新問題,Struts-Faces 的默認行為是讓 JSP 與表單 action 有同樣的名字。這種行為在有單一的 JSP 頁(如在前面的 Struts-Faces 例子中)時沒有問題。? 清單 3 展示了原來的 FlightSearch.jsp,讓我們繼續(xù)并像下面這樣修改 action:
?
|
?
當然,光有這種修改并不能解決問題。作了這種改變后,您就會發(fā)現(xiàn)?
STFRequestProcessor
?不能找到?
ActionForm
?。顯然還需要其他的改變。
不過,在繼續(xù)往下之前,看一下圖  5。它顯示了在呈現(xiàn)負責 Struts-Faces 表單的 faces 時相關(guān)的一系列事件。這與?
圖 3
相同,除了在?
FormComponent
?中突出顯示的方法?
createActionForm()。
?由 Struts-Faces API 提供的?
FormComponent
?類是?
javax.faces.component.UIForm
?的特殊子類,它支持請求或者會話范圍的表單 Bean。
圖 5. 呈現(xiàn) Struts-Faces 響應(yīng)
?
單擊這里
以查看該圖。
正如您所看到的,?
createActionForm()
?方法使用 action 名以從 Struts 配置文件中得到?
ActionMapping
?。因為沒有對于?
/listFlights.do
?的?
ActionMapping
?,所以 Struts 不能找到?
ActionForm。
這個問題的解決方法是使用?
org.apache.struts.util.RequestUtils
?。?
RequestUtils
?中的 static 方法?
getActionMappingName()
?具有足夠的智能解析映射到正確?
ActionMapping
?的路徑(?
/x/y/z
)或者后綴(?
.do
)。
清單 12 以粗體顯示對?
createActionForm
?方法的改變。我們沒有對 Struts-Faces 中的?
FormComponent
?作這些改變,而是通過繼承?
FormComponent
?并覆蓋?
createActionForm()?
方法創(chuàng)建了一個新的?
STFFormComponent。
清單 12. FormComponent 中修改過的 createActionForm() 方法
|
?
對新的?
STFFormComponent
?還要作一項改變。Struts-Faces 將 action 名本身作為表單名。這需要改變,因為 action 帶有后綴?
.do
,而表單名沒有后綴?
.do
。所以我們在?
STFFormComponent
?上增加一個名為?
action
的新屬性,并覆蓋?
getAction()
?和?
setAction()
?方法。
必須對?
FormRenderer
?(以 HTML 格式呈現(xiàn) Struts-Faces 表單的類)的?
encodeBegin
?方法進行類似于?
清單 10
所示的修改。
同樣,通過繼承?
FormRenderer
?做到這一點。此外,還必須改變寫出到 HTML 的表單 action。清單 13以粗體詳細列出了這些改變:
|
?
FormTag
的改變?
正如您已經(jīng)知道的,當組件和 renderer 改變時,標記也必須改變。在這里,通過繼承 Struts-Faces 中的?
FormTag
?創(chuàng)建一個新的標記:?
STFFormTag
?。不必改變?nèi)魏喂δ埽灰采w?
getComponentType()
?和?
getRendererType()
?方法。清單 14 展示了從?
STFFormComponent
?覆蓋的方法:
|
?
自定義組件和 renderer 必須在 faces-config.xml 文件中聲明,這樣 JSF 框架才可以初始化并使用它們。現(xiàn)在我們已經(jīng)創(chuàng)建了一個新組件?
STFFormComponent
?和一個新 renderer?
STFFormRenderer
?。
現(xiàn)在我們將在 faces-config.xml 文件中增加一個聲明,如清單 15 所示。?
component-class
?
是組件的完全限定類名。?
component-type
?指的是在?
STFFormTag
?(?
清單 12
)中用于標識組件的名字。以類似的方式發(fā)現(xiàn)和解釋 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。從 struts-faces.jar 文件中取出這個文件并將它放到 Web 應(yīng)用程序的?
WEB-INF
文件夾中并修改它。
清單 15. 在 faces-config.xml 中聲明自定義組件和 renderer
|
?
您不會在這個示例 Struts-Faces 應(yīng)用程序中看到 struts-faces.tld 文件,它打包到了 struts-faces.jar 文件中。打開并分析這個文件。它聲明了一個名為?
org.apache.struts.faces.taglib.LifecycleListener
?的類,這個類實現(xiàn)了?
ServletContextListener
?并初始化?
FacesRequestProcessor
?。
因為希望使用新的?
STFRequestProccessor
?,所以必須將這個文件從 struts-faces.jar 文件中刪除,將它放到 Web 應(yīng)用程序的?
WEB-INF
?文件夾中,并刪除偵聽器聲明。如果讓這個 tld 文件保持原樣,那么在初始化這個 Web 應(yīng)用程序時,除了?
STFRequestProcessor
?,還會實例化一個?
FacesRequestProcessor。
?
|
?
程序所有頁面瀏覽都是相對于布局頁面計算的。如果加入的?
base href
?標記只達到 Web 應(yīng)用程序上下文則會很方便,像這樣:
?
|
?
我們可以通過定制 Struts-Faces?
BaseTag
?做到這一點。這個類中的改變相當微不足道。只須在 base href 中去掉?
HttpServletRequest.getServletPath()
?。
因為這些改變是與顯示相關(guān)的,所以為它創(chuàng)建了一個名為?
STFBaseRenderer
?的新 renderer。這個新標記稱為?
STFBaseTag
?,它聲明?
STFBaseRenderer
?作為其關(guān)聯(lián)的 renderer。不需要新的組件。
有了這些信息,通過繼承?
BaseTag
?并覆蓋?
getRendererType
?方法創(chuàng)建新的?
STFBaseTag
?,如下所示:
?
|
?
恭喜!經(jīng)過這些相對較小的修改,您已經(jīng)成功地集成了 Struts、Tiles 和 JSF,并保留了您以前在這些技術(shù)上所做的所有投資。本文演示了如何將 JSF 強大的前端能力、 Tiles 的內(nèi)容格式編排優(yōu)勢以及 Struts 控制器層的靈活性結(jié)合在一個包中,使得創(chuàng)建一個 J2EE Web 應(yīng)用程序成為一項更容易的任務(wù)。
padding-top: 0px; padding-right: 0px; padding-left: 0px; padding-bottom: 6px; clear: both; text-align: left; margin-top: 0px; margin-right: 0px; marg
- 2009-03-05 16:08
- 瀏覽 360
- 評論(0)
- 相關(guān)推薦
發(fā)表評論
- 瀏覽: 30693 次
-
性別:
- 來自: 深圳
-
最近訪客 更多訪客>>
最新評論
-
hjing315
: 還是不行啊 2種方式都試過了
no XXX in java.library.path的解決辦法 -
goodayforme
: 還有個解決方案 直接把××.dll文件復制放置到j(luò)re/bin ...
no XXX in java.library.path的解決辦法 -
chunming7
: 也有可能是在catalina里設(shè)置的jvm內(nèi)存超過了機器內(nèi)存的 ...
tomcat啟動一閃而過 -
wushanlang
: 樓主?? 那如果 是部署的時候報這個錯呢
no XXX in java.library.path的解決辦法
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

評論