Apache MyFaces項(xiàng)目包含了幾個(gè)關(guān)于JavaServer技術(shù)的子項(xiàng)目。如果需要了解更多關(guān)于JavaServer Faces的知識(shí),請參考MyFaces對于JSF的介紹。
Apache MyFaces項(xiàng)目提供了以下的功能: JavaServer Faces的實(shí)現(xiàn)(MyFaces API和MyFaces Impl模塊) 用于構(gòu)建JSF相關(guān)的web應(yīng)用程序的組件庫,例如MyFaces Tomahawk、MyFaces Trinidad、MyFaces Tobago。 JavaServer Faces擴(kuò)展包,例如MyFaces Orchestra。 與其他技術(shù)和標(biāo)準(zhǔn)的集成模塊。(用于開發(fā)移動(dòng)客戶端,在本文和后續(xù)文章中對這部分功能不進(jìn)行介紹。) 可以在如下位置下載和MyFaces Impl及其各個(gè)子項(xiàng)目的發(fā)布包及樣例程序。MyFaces API 中國網(wǎng)管聯(lián)盟www、bitsCN、com 在JSF的子項(xiàng)目中,Core子項(xiàng)目視JSF規(guī)范的一個(gè)實(shí)現(xiàn),其他的項(xiàng)目實(shí)現(xiàn)了相關(guān)的規(guī)范,例如Portlet Bridge,或者為其他的JSF實(shí)現(xiàn)添加了擴(kuò)展,注意,不僅僅是MyFaces Core。同時(shí),MyFaces的擴(kuò)展,例如Tomahawk,可以和任意的JSF實(shí)現(xiàn)協(xié)同工作,例如Sun Reference Inplementation。 網(wǎng)管網(wǎng)bitsCN_com
JSF FAQ 中國網(wǎng)管聯(lián)盟www、bitsCN、com MyFaces FAQ 1. 什么是shared項(xiàng)目? 如果myfaces-core、tomahawk、tobago、trinidad都是完全獨(dú)立的項(xiàng)目,那么就不需要shared代碼項(xiàng)目,每個(gè)項(xiàng)目都在各自的名稱空間內(nèi)維護(hù)代碼。但是,會(huì)存在大量的重復(fù)代碼,并且工作量的浪費(fèi)。因?yàn)樗鼈兌紝儆趍yfaces項(xiàng)目,所以多個(gè)子項(xiàng)目可以共用的代碼放在shared項(xiàng)目中,以便減少開發(fā)和維護(hù)的工作量。 但是,這些子項(xiàng)目又都有各自的獨(dú)立釋放周期。并且,tomahawk/tobago/trinidad都應(yīng)該運(yùn)行在任意的JSF實(shí)現(xiàn)上,而不僅僅是myfaces-core。目前使用的解決辦法是重命名shared的類的報(bào)名。代碼被重命名成org.apache.myfaces.shared_impl、org.apache.myfaces.shared_tomahawk等等。這樣每個(gè)子項(xiàng)目就可以在發(fā)布的時(shí)候包含所有的支持類,而不是一個(gè)獨(dú)立的共享jar文件,也不需要關(guān)心相同類的定義的沖突。對于特定項(xiàng)目的升級(jí)不會(huì)影響到相同環(huán)境下的其他項(xiàng)目。 注意最近的MyFaces項(xiàng)目的釋放都是用了shared庫,并在源代碼的jar文件中包含了shared項(xiàng)目的源代碼,所以不需要添加額外的源代碼jar文件。 2. 如何從MyFaces獲取格式良好的HTML輸出? 中國網(wǎng)管聯(lián)盟www_bitscn_com JTidy項(xiàng)目提供了一個(gè)ServletFilter,可以將響應(yīng)的消息在輸出前進(jìn)行重新的格式化。Mozilla Firefox瀏覽器提供了一個(gè)擴(kuò)展的View Formatted Source功能。 在一些版本的MyFaces中,可以通過設(shè)置org.apache.myfaces.PRETTY_HTML來在web.xml文件中啟用pretty輸出。但是,這個(gè)選項(xiàng)從來都沒有被很好的支持,因?yàn)樾枰械膔enderer來支持,以便其工作??赡茉谝院蟮腗yFaces發(fā)布中移除。 3. MyFaces Core和tomahawk發(fā)布包中的版本號(hào)表示什么? MyFaces Core使用三個(gè)部分來表示版本,例如1.1.1。但是這個(gè)值和普通的版本號(hào)計(jì)數(shù)是不同的。強(qiáng)兩個(gè)數(shù)字表示了JSF規(guī)范的版本。因?yàn)槎M(jìn)制的JSF規(guī)范的API沒有改變,前兩位數(shù)字相同的發(fā)布包被認(rèn)為是兼容的,所有使用JSF指定特征的既存代碼會(huì)同樣可以使用。 Tomahawk庫也適用相同格式的版本號(hào)。但是因?yàn)镴SF 1.2規(guī)范是向后兼容的,即兼容JSF 1.1規(guī)范,所有Tomahawk發(fā)布的版本中,1.1.x同樣可以工作在JSF 1.2上。注意,tomahawk釋放不保證二進(jìn)制向后兼容。 4. 為什么DataModel不是可序列化的? 網(wǎng)管聯(lián)盟www.bitsCN.com DataModel類(在UIData組件中使用)在顯示和恢復(fù)視圖階段不需要保存任何任何狀態(tài)。因此,不需要將它定義為可序列化的。 如果需要定義可序列化的managed bean,并且它包含一個(gè)DataModel類型的成員變量,那么將成員變量定義為transient。 5. 為什么時(shí)間顯示不正確? JSF規(guī)范要求默認(rèn)的date->String轉(zhuǎn)換器使用標(biāo)準(zhǔn)的UTC時(shí)區(qū),也叫做GMT時(shí)區(qū)。 MyFaces 1.1.0或者早期的發(fā)布并沒有遵循JSF規(guī)范,它們默認(rèn)的使用了服務(wù)器的時(shí)區(qū)。 可以通過顯示試用轉(zhuǎn)換器來進(jìn)行時(shí)區(qū)的控制,例如: <f:convertDateTime timeZone="Antarctica/South_Pole" .../> 或者 <f:convertDateTime timeZone="#{bean.timeZone}" .../> #{bean.timeZone}返回字符串id或者TimeZone實(shí)例。 當(dāng)然,也可以注冊自定義的converter來覆蓋標(biāo)準(zhǔn)的converter,使自定義代碼適用于所有的時(shí)間到字符串的轉(zhuǎn)換。 6. 如何在一個(gè)managed bean中訪問另外一個(gè)managed bean? 54ne.com 有兩種方法來實(shí)現(xiàn)訪問同一個(gè)webapp中的其他managed bean: 使用依賴注入:在faces配置文件中定義managed beans,managed bean的屬性可以被聲明成到其他managed bean的引用: <managed-bean> <managed-bean-name>neededBean</managed-bean-name> <managed-bean-class>fqn.to.NeededBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> m
<managed-bean> 中國網(wǎng)管聯(lián)盟www、bitsCN、com
</managed-property> </managed-bean> JSF規(guī)范要求managed properties根據(jù)它們聲明的順序進(jìn)行初始化。所以setInitialized方法會(huì)在所有其他屬性被調(diào)用后進(jìn)行設(shè)置。 8. PhaseListener為什么會(huì)被調(diào)用兩次?
JSF規(guī)范要求任何JSF實(shí)現(xiàn)框架在啟動(dòng)時(shí)自動(dòng)加載/WEB-INF/faces-config.xml,所以沒有必要添加如下的context參數(shù): <context-param> <param-name>javax.faces.CONFIG_FILES</param-name> <param-value>/WEB-INF/faces-config.xml</param-value> </context-param> 如果在web.xml文件中配置了上述信息,會(huì)迫使JSF實(shí)現(xiàn)加載配置文件兩次,所以注冊了每個(gè)phase listener兩次。 9. dataTables的Action listener和action命令沒有被調(diào)用? 如果action源(h:commandLink,h:commandButton)沒有被提供,那么Action Listeners和actions就不會(huì)被激活。當(dāng)action源在dataTable上時(shí),并且dataTable的value屬性指向一個(gè)請求范圍的數(shù)據(jù)源,那么action源在接下來的請求中就沒有被提供,例如: <h:dataTable value="#{requestScopedBean.dataModel.wrappedData}" /> <h:column> <h:commandLink value="click here" action="#{backingBean.willNotFire}" /> </h:column> </h:dataTable> action源沒有被rendered,是因?yàn)閿?shù)據(jù)源在隨后的請求中不存在了(在第一次響應(yīng)完成后,被垃圾回收器進(jìn)行了回收)。 為了解決這個(gè)問題,使用t:saveState標(biāo)記或者將request范圍的bean放在session范圍內(nèi): <t:saveState value="#{myRequestScopedBean.dataModel.wrappedData}" /> 10. 日歷、樹等不能工作,并發(fā)生javascript腳本錯(cuò)誤? 這是需要配置MyFacesExtensionFilter。 一些MyFaces組件不僅僅包含了HTML,可能需要額外的支持腳本、樣式表、圖片等等。這些資源包含在MyFaces的jar文件中,Extensions Filter添加所需的代碼和URL來提供這些資源給生成的HTML。 網(wǎng)管聯(lián)盟www.bitsCN.com
一些其他的組件,例如文件上傳需要解析Multipart請求,這也是由Extensions Filter來完成的。 使用ExtensionFilter有如下的好處: 將MyFaces的組件和應(yīng)用程序良好的隔離 不需要在頁面或者webapp中添加MyFaces其他的組件相關(guān)的代碼或者資源。 為MyFaces開發(fā)組提供了靈活的更新組件,保持透明及向后兼容的功能。 為頁面開發(fā)人員減輕了壓力。 只加載對使用組件的資源 處理MyFaces資源緩存 可以通過如下方式配置extension filter: 在web.xml文件中,將filter映射到JSF頁面,例如*.jsp,以便使filter可以更新頁面中的資源鏈接;同時(shí)映射filter到/faces/myFaces/ExtensionResources/*路徑,這樣可以處理頁面獨(dú)立的資源,例如圖片,javascript腳本文件以及樣式表等等。下面是一個(gè)配置的例子: <filter> <filter-name>MyFacesExtensionsFilter</filter-name> <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class> 54ne.com
<init-param> <param-name>maxFileSize</param-name> <param-value>20m</param-value> <description>Set the size limit for uploaded files. Format: 10 - 10 bytes 10k - 10 KB 10m - 10 MB 1g - 1 GB </description> feedom.net </init-param> </filter> 網(wǎng)管網(wǎng)bitsCN_com
<!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages --> 網(wǎng)管聯(lián)盟www.bitsCN.com
<!-- extension mapping for serving page-independent resources (javascript, stylesheets, images, etc.) --> 中國網(wǎng)管論壇bbs.bitsCN.com
如果只是使用標(biāo)準(zhǔn)的JSF組件,而不是用MyFaces擴(kuò)展的組件,以t:打頭的,那么就不需要使用這個(gè)filter,否則的話,就需要進(jìn)行配置。 11. “ExtensionFilter not correctly configured.” Error? 如果發(fā)生如下的錯(cuò)誤: "java.lang.IllegalStateException: ExtensionsFilter not correctly configured. JSF mapping missing. JSF pages not covered" 并且所有的配置已經(jīng)正確的設(shè)置,那么檢查以下內(nèi)容: 確保配置正確,參考:http://myfaces./tomahawk/extensionsFilter.html 如果使用Servlet 2.4,那么不能使用jsp:forward或者request.getDispatcher().forward來跳轉(zhuǎn)到某個(gè)頁面,因?yàn)闆]有執(zhí)行extensions filter,作為替代方法,要使用response.sendRedirect方法。 12. 使用tomahawk:popup標(biāo)記時(shí),發(fā)生NullPointerException,在HtmlPopupRenderer.encodedEnd facet的名字需要硬編碼成”popup”。 庫的依賴情況 MyFaces核心包和組件庫。 當(dāng)發(fā)布MyFaces類庫的時(shí)候,也就是說不是Core框架,它會(huì)和當(dāng)前的MyFaces Core,Sun Mojarra(即Sun JSF RI)的釋放版本兼容。 54com.cn
同時(shí)支持一些其他的版本,但是不做任何保證。 Getting Started 開始Apache MyFaces的第一步應(yīng)該是查看一下樣例應(yīng)用程序??梢栽趆ttp://www./myfaces.jsf查看這些程序,或者可以通過自定義部署來完成??梢酝ㄟ^以下方式來部署: 下載Tomcat5.x/Tomcat6.x MyFaces例子。下載最新的webapp文件(tomahawk-X.X.X-examples.zip) 將MyFaces樣例文件解壓縮到指定目錄。 將任何之前在Tomcat中發(fā)布過的MyFaces應(yīng)用程序清除,同時(shí)清空Tomcat的work目錄。并且保證類路徑或者Tomcat的lib(common/lib or shared/lib)中不存在jsf-api.jar或者jsf-impl.jar(也就是說Sun API和實(shí)現(xiàn))。將simple.war文件或者其他的例子拷貝到Tomcat安裝目錄的webapps目錄。啟動(dòng)Tomcat。 也可以使用MyFaces 運(yùn)行Sun JSF RI樣例程序,具體介紹參考這里。 如何在自定義的web應(yīng)用程序中使用MyFaces?建議步驟: 在MyFaces Wiki網(wǎng)站查看與開發(fā)環(huán)境中servlet容器的兼容性 然后可以使用樣例程序來開始開發(fā),例如blank.war。將blank.war解壓縮后,就構(gòu)成了需要工作的目錄結(jié)構(gòu)。 中國網(wǎng)管聯(lián)盟www_bitscn_com 安裝和配置 1. 在沒有網(wǎng)絡(luò)連接的情況下使用Tomahawk tomahawk.jar文件包含了META-INF/faces-config.xml,該文件使用了到web-facesconfig_1_1.dtd的PUBLIC引用。這使得在應(yīng)用程序服務(wù)器啟動(dòng)的時(shí)候,訪問java.sun.com來參考該DTD文件。這個(gè)問題的顯示如下(從Tomcat日志中獲取): “javax.faces.FacesException:Can’t parse configuration file:jar:file:/<web-context-path>/WEB-INF/lib/tomahawk.jar !/META-INF/faces-config.xml” 解決這個(gè)問題的辦法如下: 在tomahawk.jar文件中添加META-INF/web-facesconfig_1_1.dtd 修改同目錄的faces-config.xml的到DTD的引用: <!DOCTYPE faces-config SYSTEM "web-facesconfig_1_1.dtd"> 2. Apache Tomcat作為Servlet容器。 Apache Tomcat 5.5.x(不包括5.5.9) Apache Tomcat 5.5.x可以和MyFaces共同工作,所有需要的jar文件已經(jīng)在MyFaces提供的war文件中包含。 54ne.com 如果在啟動(dòng)時(shí)看到空白頁面,那么需要移除jsp-2.0.jar和commons.el。因?yàn)門omcat5.5.x將這些文件放在容器外,造成了和MyFaces的war文件提供的jar文件沖突。 54ne.com
專題項(xiàng)目 網(wǎng)管網(wǎng)bitsCN.com
將輸入組件設(shè)置為immediate并不影響模型的更新,任何的新的值仍然會(huì)在Update Model階段被注入(也就是說,在任何immediate命令組件執(zhí)行之后)。注意,也可以使用ValueChangeListener直接更新模型。 使用immediate屬性使組件ActionListener或者action方法在apply-request-values階段的最后被執(zhí)行。也就是說,在任意非immediate值的驗(yàn)證和后臺(tái)bean被更新之前執(zhí)行。 如果是返回一個(gè)導(dǎo)航字符串的form的action方法,那么: 任意非null字符串會(huì)使得生命周期直接運(yùn)行到render-response階段,意味著任意非immediate組件的驗(yàn)證永遠(yuǎn)不會(huì)被執(zhí)行。這就是為什么immediate命令組件會(huì)以自然的方式來實(shí)現(xiàn)cancel操作。它甚至在頁面中輸入域驗(yàn)證失敗的情況。當(dāng)然,也就沒有什么update model階段,也就是說用戶輸入的數(shù)據(jù)被丟棄。 null返回值會(huì)導(dǎo)致處理正常進(jìn)行,也就是說,非immediate組件被驗(yàn)證,然后執(zhí)行update model(如果不發(fā)生驗(yàn)證錯(cuò)誤)。 如果想讓actionListener方法返回void,必須調(diào)用 facesContext.renderResponse(); 在使用immediate輸入組件時(shí)最重要的問題就是用戶新輸入的數(shù)值并不是總能在model中訪問,因?yàn)閡pdate-model階段還沒有執(zhí)行。 網(wǎng)管網(wǎng)bitsCN.com
對于頁面中的非immediate輸入組件,immediate命令組件的action方法訪問用戶輸入數(shù)據(jù)的唯一方式就是通過使用組件綁定和通過名稱查詢來獲取指定的UIComponent對象,然后調(diào)用getSubmittedValue方法來獲取用戶提供的原始字符串。這個(gè)值沒有被轉(zhuǎn)換成它的目標(biāo)類型,也不會(huì)被驗(yàn)證。 對于immediate輸入組件,進(jìn)行了轉(zhuǎn)換和驗(yàn)證的步驟,使用對應(yīng)的UIComponnet組件,是可能獲取轉(zhuǎn)換后的值。如果組件在頁面中位于UICommand組件的前面,并且觸發(fā)了ValueChangeListener,這樣就會(huì)執(zhí)行ValueChangeListener。 警告:如果action方法更新模型,但是不進(jìn)行導(dǎo)航,那么在輸入組件的值通過驗(yàn)證并更新模型時(shí)都會(huì)覆蓋后臺(tái)bean的值。 任何immediate 組件的驗(yàn)證失敗都不會(huì)停止immediate命令組件的執(zhí)行,這和non-immediate輸入組件和命令組件大不相同。 學(xué)習(xí)指南 當(dāng)學(xué)習(xí)JSF的時(shí)候,不需要仔細(xì)查看具體的實(shí)現(xiàn),下面建議了一個(gè)類/方法的列表,對學(xué)習(xí)JSF和MyFaces如何實(shí)際工作的很有幫助,同時(shí)提到了對典型方法的一個(gè)簡短描述。 javax.faces.webapp.FacesServlet init方法用于啟動(dòng)基本的faces。它演示了如何使用FactoryFinder來創(chuàng)建LifeCycle和FacesContext工廠 網(wǎng)管網(wǎng)bitsCN.com service方法演示了LifeCycle對象控制整個(gè)JSF處理 javax.faces.component.UIViewRoot queueEvent方法在組件決定激活一個(gè)value-change事件(或者其他類型的事件)時(shí)被調(diào)用。事件隊(duì)列的有意思的地方是: javax.faces.component.UIInput的驗(yàn)證方法 org.apache.myfaces.renderkit.html.HtmlButtonRendererBase的decode方法 javax.faces.component.UIComponentBase getRenderer方法演示了如何使用當(dāng)前視圖中的renderkit-id、組件聲明的組件家族和組件聲明的renderer-type名稱來決定使用哪一個(gè)renderer。
javax.faces.webapp.UIComponentTag createComponentInstance演示了在JSP標(biāo)記引用UIComponent組件并且組建不存在于view眾時(shí),如何實(shí)例化這個(gè)組件,通過調(diào)用Application.createComponent(String),注意該createComponentInstance方法調(diào)用自己的getComponentType()方法,這個(gè)方法典型的實(shí)現(xiàn)了JSF終端標(biāo)記類,例如org.apache.myfaces.taglib.html.HtmlCommandButtonTag。 javax.faces.componnet.UIInput processDecodes、processValidators、processUpdates方法演示了表單數(shù)據(jù)是如何變成模型的數(shù)據(jù),通過轉(zhuǎn)換和驗(yàn)證的處理。對于多數(shù)的組件,實(shí)際的工作過程為Apply Values、Process Validators和Update Model階段。注意,數(shù)據(jù)從表單中開始,加載到組件的提交值字段,轉(zhuǎn)換成組件的本地值字段,然后拷貝到后臺(tái)bean。很多條件影響到process的出口,例如rendered state、immediate state、conversion errors和validation errors。 結(jié)合DataTable和ActionListeners 網(wǎng)管網(wǎng)bitsCN_com 如果在dataTable的某一行包含command link或者command button,可以從javax.faces.event.ActionListener中輕松的獲取bean: <h:dataTable value="#{ResultsBean.hitSet.hits}" var="hit"> <h:column> <h:commandLink> <f:actionListener type="net.java.OrderActionListener" /> <h:outputText value="Order" /> </h:commandLink> ... </h:column> </h:dataTable> 可以通過下面的java代碼來獲取bean: public class OrderActionListener implements ActionListener { public void processAction(ActionEvent anEvent) throws AbortProcessingException { YourBeanClass tmpBean = null; // 事件的getComponent方法返回command link或者command button UIComponent tmpComponent = anEvent.getComponent(); // 遍歷command link或者command button的父組件,出口:父組件為UIData while (null != tmpComponent && !(tmpComponent instanceof UIData)) { tmpComponent = tmpComponent.getParent(); } // 如果事件組件不為空,并且父組件為UIData,那么取每行的數(shù)據(jù),如果是Bean實(shí)例,那么強(qiáng)制轉(zhuǎn)換 if (tmpComponent != null && (tmpComponent instanceof UIData)) { Object tmpRowData = ((UIData) tmpComponent).getRowData(); if (tmpRowData instanceof YourBeanClass) { 網(wǎng)管網(wǎng)bitsCN_com tmpBean = (YourBeanClass) tmpRowData; //TODO Implementation of your method } } //TODO Exception Handling if UIData not found or tmpRowBean of wrong type } } 通過Link或者Button的參數(shù)來執(zhí)行方法 一個(gè)典型的情景: 表格顯示了對象的集合,需要通過點(diǎn)擊edit link或者button來跳轉(zhuǎn)到要編輯的記錄的詳細(xì)頁面。 如果熟悉Struts或者其他的MVC框架,那么可能會(huì)考慮到傳輸一個(gè)主鍵來作為請求參數(shù),在請求頁面的URL中被包含。 <a href="/appContext/someAction.do?id=1234&userAction=prepareEdit">Edit</a> 可以使用JSTL來生成上面的內(nèi)容: <c:url value="someAction.do" var="url"> feedom.net <c:param name="id" value="1234" /> <c:param name="userAction" value="prepareEdit" /> </c:url> <a href="${url}">Edit</a> 在JSF中,有很多方法來處理這種情況,下面列舉了三種,關(guān)于第一種解決方式還存在爭議,這里列舉出來的目的是這可能是開發(fā)人員最先想到的。 1> 使用<f:factionListener ...>和UIData的getRowData() 可以參考上面的集成DataTable和ActionListeners來完成。 2> 使用f:param來傳遞參數(shù) 使用JSF完成的第一個(gè)思路可能就是模擬已經(jīng)將參數(shù)傳遞給了鏈接??梢酝ㄟ^在commandButton或者commandLink中使用f:param標(biāo)記: <t:dataTable var="emp" .... >
<h:commandLink id="editLink" action="#{employeeAction.prepareEdit}">
然后獲取處理請求參數(shù)的句柄:
FacesContext context = FacesContext.getCurrentInstance(); |
|