<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    itVincent Blog - Java Working Fun!

    技術(shù)引領(lǐng)時(shí)代!
    posts - 117, comments - 181, trackbacks - 0, articles - 12

    [轉(zhuǎn)]Facelets 非常適合 JSF

    Posted on 2006-11-01 09:09 itVincent 閱讀(916) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JSF

    Facelets 非常適合 JSF

    最終形成了專(zhuān)為 JSF 設(shè)計(jì)的視圖技術(shù)!

    ?

    ?

    未顯示需要 JavaScript 的文檔選項(xiàng)


    ?


    級(jí)別: 初級(jí)

    Richard Hightower, 開(kāi)發(fā)人員, ArcMind Inc.

    2006 年 3 月 27 日

    試圖把 JSF 和 JSP 結(jié)合起來(lái)就像試圖要把腳硬塞進(jìn)手套一樣:可能做得到,但是只是更好的解決辦法出現(xiàn)之前的一個(gè)權(quán)宜之計(jì)。在這篇文章中,JSF 的熱心支持者 Rick Hightower 介紹了關(guān)于 Facelets 他最喜歡的內(nèi)容:容易的 HTML 樣式的模板化和可重用的復(fù)合組件。

    由于最近在 Java? 服務(wù)器外觀(JSF)項(xiàng)目上工作,我很有幸第一次使用了 Facelets。關(guān)于 Facelets,我最喜歡的是它讓我可以創(chuàng)建可重用的復(fù)合組件。能夠拿出一個(gè)頁(yè)面(例如 JSP)并把它變成組件,對(duì)于我的 JSF 開(kāi)發(fā)來(lái)說(shuō)真是莫大的好處。我的結(jié)論是什么?如果不用 Facelets,那么就無(wú)法得到能從 JSF 獲得的最大收獲。

    JSF 和 Java 服務(wù)器頁(yè)面技術(shù)之間的不匹配,是 JSF 開(kāi)發(fā)中的一個(gè)嚴(yán)重問(wèn)題。問(wèn)題是如何把 JSP 的動(dòng)態(tài)內(nèi)容集成到 JSF 基于組件的模型中。JSP 非常重視生成動(dòng)態(tài)內(nèi)容輸出,而 JSF 需要 JSP 來(lái)協(xié)調(diào)組件模型的構(gòu)建。因?yàn)檫@個(gè)任務(wù)超出了 JSP 原來(lái)的目的,所以產(chǎn)生了距離。

    大多數(shù) JSF 開(kāi)發(fā)人員只是學(xué)會(huì)了一事一議地解決這類(lèi)問(wèn)題,但是這就像在錘子上放一個(gè)枕頭,最終還會(huì)掉下來(lái)打傷腦袋。Facelets 是更加全面的解決方案:專(zhuān)為 JSF 組件模型度身定制的模板化語(yǔ)言。

    Facelets 有以下吸引人的特性:

    • 模板化(像 Tiles)
    • 復(fù)合組件
    • 定制的邏輯標(biāo)記
    • 表達(dá)式語(yǔ)言
    • 對(duì)設(shè)計(jì)師友好的頁(yè)面開(kāi)發(fā)
    • 創(chuàng)建組件庫(kù)

    這些特性比我想像的要更相關(guān)和統(tǒng)一。在這篇文章中,我討論前兩個(gè):模板化和復(fù)合組件。我使用的 Web 應(yīng)用程序基于為我的針對(duì)懷疑者的 JSF 系列開(kāi)發(fā)的一個(gè)應(yīng)用程序,我把它更新成使用 Facelets 視圖而不是 Tiles。在進(jìn)一步閱讀之前,應(yīng)當(dāng) 下載示例代碼。如果要隨著討論一起操作,還需要 安裝 Facelets

    Facelets 概述

    對(duì)于 Facelets 可能會(huì)做的最大一個(gè)錯(cuò)誤假設(shè),就是它只是 Tiles 的替代品。Facelets 遠(yuǎn)不止如此:它是思考 JSF 的新方式。

    選擇標(biāo)記

    雖然多數(shù)開(kāi)發(fā)人員把 Facelets 用于 XHTML,實(shí)際上這個(gè)框架并不在意使用什么標(biāo)記:它與 XUL (XULFaces)兼容, Kito Mann 已經(jīng)用它為 JSF 中心提供 RSS。

    JSP 是種生成 servlet 的模板化語(yǔ)言。JSP 的主體與 servlet 的 doGet()doPost() 方法等價(jià)(也就是說(shuō),成為 jspService() 方法)。JSF 定制標(biāo)記(例如 f:viewh:form)只是調(diào)用 JSF 組件來(lái)呈現(xiàn)它們自己的當(dāng)前狀態(tài)。JSF 組件模型的生命周期獨(dú)立于 JSP 生成的 servlet 的生命周期。這種獨(dú)立性就是混淆的來(lái)源。

    與 JSP 不同,F(xiàn)acelets 這個(gè)模板化語(yǔ)言,從構(gòu)建之初,就考慮了 JSF 的組件生命周期。使用 Facelets,生成的模板會(huì)構(gòu)建組件樹(shù),而不是 servlet。這就允許更好的重用,因?yàn)榭梢园呀M件組合成另一個(gè)組件。

    Facelets 減少了編寫(xiě)定制標(biāo)記才能使用 JSF 的需求。Facelets 本身就可以使用 JSF 定制組件。溝通 JSF 和 Facelets 只需要很少的特殊編碼:要做的全部工作就是在 Facelet 標(biāo)記庫(kù)文件中聲明 JSF 組件。在 Facelets 模板化語(yǔ)言中可以直接使用 JSF 組件,不用任何額外的開(kāi)發(fā)。

    Facelets 模板框架

    在提供針對(duì)組件構(gòu)建設(shè)計(jì)的模板框架方面,F(xiàn)acelets 與 Tapestry (請(qǐng)參閱 參考資料)類(lèi)似。但是,對(duì)于具有 JSP 背景的我們來(lái)說(shuō),F(xiàn)acelets 看起來(lái)比 Tapestry 友好得多。它允許使用熟悉的 JSTL 樣式的標(biāo)記和 JSTL/JSF/JSP 樣式的表達(dá)式語(yǔ)言。大大降低的學(xué)習(xí)曲線意味著可以更加迅速地開(kāi)始開(kāi)發(fā)。

    Facelets 和 Tapestry

    Facelets 與 Tapestry 很相似,可以相互比較。實(shí)際上,Tapestry 剛出現(xiàn)的時(shí)候,大大領(lǐng)先于它的時(shí)代,而 Facelets 確實(shí)借鑒了它的一些想法。但是,如果只把 Facelets 當(dāng)成 JSF 版本的 Tapestry,那就錯(cuò)了。這兩項(xiàng)技術(shù)是不同的。要了解關(guān)于 Tapestry 的更多內(nèi)容,請(qǐng)參閱 Brett McLaughlin 兩部分的系列 “了解 Tapestry,第 1 部分”。

    Facelets 允許定義能夠直接包含進(jìn)頁(yè)面或者容易地添加到 Facelet 標(biāo)記庫(kù)的組件集。實(shí)際上讓人高興的是在 Facelets 中定義定制標(biāo)記(復(fù)合組件和類(lèi)似 JSP 定制標(biāo)記的標(biāo)記)的迅速。使用這些組件集,F(xiàn)acelets 還允許定義站點(diǎn)模板(和更小的模板)。這與使用 Tiles 很相似,但是少了定義文件。也可以在定制 JSF 組件內(nèi)部使用 Facelets,因?yàn)?Facelets API 提供了可以容易地與 JSF 組件集成的接口。

    從 Tiles 到 Facelets

    如前所述,在這里使用的示例 Web 應(yīng)用程序基于為我的 針對(duì)懷疑者的 JSF 系列創(chuàng)建的示例。它為一家在線 CD 店管理庫(kù)存,創(chuàng)建、讀取、更新和刪除(CRUD)清單。它包含一個(gè)表單,讓用戶向系統(tǒng)輸入新 CD,有一個(gè)單選按鈕列表,允許用戶選擇音樂(lè)分類(lèi)。當(dāng)用戶選擇了一個(gè)分類(lèi)時(shí),就觸發(fā)某些 JavaScript 立即把表單提交回服務(wù)器。應(yīng)用程序還包含一個(gè) CD 清單,用戶可以根據(jù)標(biāo)題或藝術(shù)家對(duì)清單中的 CD 排序。圖 1 是應(yīng)用程序類(lèi)的 UML 圖表:


    圖 1. 在線 CD 商店示例的類(lèi)圖
    示例應(yīng)用程序類(lèi)

    圖 2 提供了商店的 CD 清單頁(yè)面:


    圖 2. 在線 CD 商店的清單頁(yè)面
    CD 清單

    原來(lái)的應(yīng)用程序從 Tiles 得到視圖支持,現(xiàn)在我將用 Facelets 構(gòu)建視圖。我先從用 Facelets 替換示例中的 Tiles 支持開(kāi)始,然后編寫(xiě)復(fù)合組件。在開(kāi)始之前,需要已經(jīng)安裝了 Facelets。

    安裝 Facelets

    安裝 Facelets 的步驟很容易。請(qǐng)注意,我假設(shè)已經(jīng)下載并安裝了 示例應(yīng)用程序

    1. 下載 Facelets 發(fā)行包 并解壓縮。

    2. 把 jsf-facelets.jar 拷貝到 WEB-INF/lib 目錄(在應(yīng)用程序部署時(shí),它最終必須放在 WEB-INF/lib 目錄中)。

    3. 把 Facelet 初始化參數(shù)添加到 web.xml 文件中。

    4. 把 FaceletViewHandler 添加到 faces-config.xml 文件中。

    步驟 1 和 2 比較基本。我將詳細(xì)介紹其他兩個(gè)步驟。

    添加初始化參數(shù)

    這一步假設(shè)已經(jīng)安裝了工作正常的 JSF 應(yīng)用程序(例如 在線 CD 商店示例),然后編輯現(xiàn)有的 web.xml 頁(yè)面,添加以下參數(shù):

    												
    														<context-param>
    	<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    	<param-value>.xhtml</param-value>
    </context-param>
    
    												
    										

    這告訴 JSF 采用 xhtml 前綴,F(xiàn)acelet 渲染器能夠解釋這個(gè)前綴。

    Facelets 有許多參數(shù),請(qǐng)參閱 參考資料 獲得完整清單。如果示例有問(wèn)題,請(qǐng)參考 DEVELOPMENT init 參數(shù),它適合調(diào)試。把 REFRESH_PERIOD 參數(shù)設(shè)置為 low 在開(kāi)發(fā)期間會(huì)有幫助。

    添加 FaceletViewHandler

    要讓 Facelets 模板生效,需要把 Facelets 視圖處理器告訴 JSF。JSF ViewHandler 是個(gè)插件,為不同的響應(yīng)生成技術(shù)(包括 Facelets)處理 JSF 請(qǐng)求處理生命周期的 “渲染器響應(yīng)和恢復(fù)視圖” 階段。(任何認(rèn)為 JSF 不能擴(kuò)展的人都是被誤導(dǎo)了!)通過(guò)添加以下視圖處理器到 faces-config.xml 中,就把 Facelets 插進(jìn)了 JSF 中:

    												
    														  <application>
        <locale-config>
          <default-locale>en</default-locale>
        </locale-config>
    	<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
      </application>
    
    												
    										







    用 Facelets 進(jìn)行模板化

    首先介紹 Facelets 模板化框架,因?yàn)樗鄬?duì)容易理解。創(chuàng)建和使用 Facelets 模板的步驟如下:

    1. 創(chuàng)建 layout.xhtml 頁(yè)面。
    2. 定義 Facelet 的命名空間,導(dǎo)入對(duì) Facelets 的使用。
    3. ui:insert 標(biāo)記定義頁(yè)面的邏輯區(qū)域。
    4. 用純文本和 ui:include 標(biāo)記定義合理的默認(rèn)值。

    我要逐步介紹這些步驟,用在線 CD 商店清單頁(yè)面作為我的布局示例。

    步驟 1. 創(chuàng)建 layout.xhtml 頁(yè)面

    layout.xhtml 頁(yè)面就是一個(gè)一般的 XHTML 文本文件,使用了以下文檔類(lèi)型聲明:

    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    </html>
    
    												
    										

    不要求進(jìn)一步細(xì)節(jié)!

    步驟 2. 定義 Facelets 的命名空間

    為了用 Facelets 標(biāo)記進(jìn)行模板化,需要用 XML 命名空間像下面這樣導(dǎo)入它們:

    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets">
    ...
    
    												
    										

    請(qǐng)注意 ui 命名空間的定義。

    步驟 3. 用 ui:insert 標(biāo)記定義頁(yè)面的邏輯區(qū)域

    下面,定義布局的邏輯區(qū)域,例如頁(yè)面標(biāo)題、小標(biāo)題、導(dǎo)航、內(nèi)容等等。下面是定義頁(yè)面標(biāo)題的示例:

    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets">
    <head>
      <title><ui:insert name="title">Default title</ui:insert></title>
      <link rel="stylesheet" type="text/css" href="./css/main.css"/>
    </head>
    ...
    
    												
    										

    請(qǐng)注意使用 ui:insert 標(biāo)記定義了標(biāo)題的邏輯區(qū)域。ui:insert 元素內(nèi)的文本 “Default title” 定義了模板用戶不傳遞標(biāo)題時(shí)顯示的文本。也可以像下面這樣編寫(xiě)上面的內(nèi)容:

    												
    														<title>#{title}</title>
    
    												
    										

    步驟 4. 用純文本和 ui:includes 定義默認(rèn)值

    可以傳遞更多的純文本作為默認(rèn)值。例如,請(qǐng)研究 layout.xhtml 中的以下代碼片段:

    												
    														<div id="header">
        <ui:insert name="header">
        	<ui:include src="header.xhtml"/>
        </ui:insert>
    </div>
    
    												
    										

    在這里,我用了 ui:insert 標(biāo)記定義邏輯區(qū)域,用 ui:include 標(biāo)記插入默認(rèn)值。默認(rèn)情況下,使用布局的頁(yè)面會(huì)采用 header.xhtml 的內(nèi)容作為標(biāo)題文本,但是因?yàn)闃?biāo)題是 ui:insert 定義的邏輯區(qū)域,所以用這個(gè)模板也能傳遞不同的標(biāo)題。對(duì)于擁有前端(例如,帶有購(gòu)物車(chē)的目錄)和后端管理(例如添加新產(chǎn)品)的應(yīng)用程序,后端站點(diǎn)在標(biāo)題或?qū)Ш缴峡赡懿煌逆溄印?code>ui:include 標(biāo)記可以容易地用新標(biāo)題換掉默認(rèn)標(biāo)題。

    清單 1 顯示了示例應(yīng)用程序的清單頁(yè)面 list.xhtml 的完整代碼:


    清單 1. 完整的 list.xhtml
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets">
    <head>
      <title><ui:insert name="title">Default title</ui:insert></title>
      <link rel="stylesheet" type="text/css" href="./css/main.css"/>
    </head>
    
    <body>
    
    <div id="header">
        <ui:insert name="header">
        	<ui:include src="header.xhtml"/>
        </ui:insert>
    </div>
    
    
    <div id="left">
      <ui:insert name="navigation" >
        <ui:include src="navigation.xhtml"/>
      </ui:insert>
    </div>
    
    
    <div id="center">
      <br />
      <span class="titleText"> <ui:insert name="title" /> </span>
      <hr />
      <ui:insert name="content">
      	<div>
        <ui:include src="content.xhtml"/>  
        </div>
      </ui:insert>
    </div>
    
    <div id="right">
      <ui:insert name="news">
        <ui:include src="news.xhtml"/>
      </ui:insert>
    </div>
    
    <div id="footer">
      <ui:insert name="footer">
        <ui:include src="footer.xhtml"/>  
      </ui:insert>
    </div>
    
    </body>
    
    </html>
    
    												
    										

    現(xiàn)在已經(jīng)知道了如何定義布局,我將介紹如何使用布局!







    使用 Facelets 模板

    為了調(diào)用模板,要使用 ui:composition 標(biāo)記。為了把參數(shù)傳遞給模板,要使用 ui:define 標(biāo)記,它是 ui:composition 標(biāo)記的子元素。在清單 2 中,我調(diào)用了在線 CD 商店示例的布局頁(yè)面:


    清單 2. 調(diào)用布局頁(yè)面
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core">
    
    
    <ui:composition template="/WEB-INF/layout/layout.xhtml">
      <ui:define name="title">CD form</ui:define>
      <ui:define name="content">
    
    	<!-- use the form tag to set up this form -->
    	<h:form id="cdForm">
    
    		...
    		...
    ...
    
    	</h:form>
      </ui:define>
    </ui:composition>
    </html>
    
    												
    										

    請(qǐng)注意在上面的調(diào)用中包含了以下命名空間:

    • xmlns:h="http://java.sun.com/jsf/html"
    • xmlns:f="http://java.sun.com/jsf/core"

    有了 Facelets,就不必依賴 JSF 標(biāo)記庫(kù),所以要使用核心和 HTML JSF 組件,必須通過(guò)以上命名空間導(dǎo)入它們。

    html 標(biāo)記的使用看起來(lái)可能有些奇怪。畢竟,清單 2 所示的布局頁(yè)面要調(diào)用的模板已經(jīng)有了一個(gè) html 標(biāo)記;所以這是不是意味著會(huì)得到兩個(gè) html 標(biāo)記?如果真的這樣,那么在 ui:composition 標(biāo)記之外的內(nèi)容全部被忽略,所以 html 標(biāo)記所做的只是讓 HTML 編輯器能夠看到 HTML 片段。它不會(huì)影響運(yùn)行時(shí)行為。

    位置是一切

    當(dāng)頁(yè)面調(diào)用布局模板時(shí),只需指定模板的位置,如下所示:

    												
    														<ui:composition template="/WEB-INF/layout/layout.xhtml">
    
    												
    										

    這個(gè)標(biāo)記調(diào)用清單 1 所示的模板,所以我要做的全部工作就是把參數(shù)傳遞給模板。然后,在復(fù)合標(biāo)記內(nèi)部,可以傳遞像標(biāo)題這樣的簡(jiǎn)單文本:

    												
    														  <ui:define name="title">CD form</ui:define>
    
    												
    										

    或者傳遞整個(gè)組件樹(shù):

    												
    														  <ui:define name="content">
    
    	<!-- use the form tag to setup this form -->
    	<h:form id="cdForm">
    
    		...
    		...
    ...
    
    	</h:form>
      </ui:define>
    
    												
    										

    請(qǐng)注意,在我定義和傳遞的許多邏輯區(qū)域中,cdForm.xhtml 只傳遞兩個(gè):內(nèi)容和標(biāo)題。

    Tiles 與 Facelets

    這篇文章帶了三個(gè)示例:第一個(gè)使用 Tiles,第二個(gè)使用 Facelets,第三個(gè)使用復(fù)合組件。之所以包含 Tiles 示例是為了可以比較和對(duì)比兩個(gè)框架中不同的模板化技術(shù)。請(qǐng)看看它,并看看自己有什么想法!

    復(fù)合組件

    如果只用 Facelets 定義和使用模板,那么可能會(huì)有點(diǎn)失敗。雖然 Facelets 的模板化特性完整而且豐富,但是它沒(méi)有 Tiles 之類(lèi)的框架那么多的特性,后者還擅長(zhǎng)定義默認(rèn)值、相關(guān)模板的層次結(jié)構(gòu)以及類(lèi)似的東西。

    但模板化不是 Facelets 真正出色的地方:Facelets 把它的精華放在復(fù)合組件上。(有趣的是,復(fù)合組件也給 Facelets 模板化帶來(lái)了一些好處;例如,在 Facelets 中可以舍棄 f:verbatim 標(biāo)記和各種 h:outputText 標(biāo)記,因?yàn)樗械臇|西都被當(dāng)成組件樹(shù)中的組件。關(guān)于這方面的更多內(nèi)容稍后介紹。)

    對(duì)于這篇文章余下的部分,我將重點(diǎn)放在創(chuàng)建和使用復(fù)合組件的步驟上。但在開(kāi)始之前,先要確保能夠清楚地理解是什么讓這些方便的小代碼段這么棒。

    破壞 DRY 原則

    您是否曾經(jīng)編寫(xiě)過(guò)像清單 3 所示的代碼片段?


    清單 3. 復(fù)合組件之前的生活
    												
    														<h:dataTable id="items" value="#{CDManagerBean.cds}" var="cd"
    	rowClasses="oddRow, evenRow" headerClass="tableHeader">
    
    <!--  Title -->
    <h:column>
    	<f:facet name="header">
    		<h:panelGroup>
    			<h:outputText value="Title" />
    				
                     <f:verbatim>[</f:verbatim>
    
    			<h:commandLink styleClass="smallLink"
                                  action="#{CDManagerBean.sort}">
    				<h:outputText id="ascTitle" value="asc" />
    				<f:param name="by" value="title"/>
    				<f:param name="order" value="asc"/>
    			</h:commandLink>
    
    			<h:outputText value="," />
    			<!-- Sort descending -->
    			<h:commandLink styleClass="smallLink"
                             action="#{CDManagerBean.sort}">
    				<h:outputText id="decTitle" value="dec" />
    				<f:param name="by" value="title"/>
    				<f:param name="order" value="dec"/>
    			</h:commandLink>
    			     <f:verbatim>]</f:verbatim>
    		</h:panelGroup>
    	</f:facet>
    
    	<h:outputText value="#{cd.title}" />
    </h:column>
    
    <!--  Artist -->
    <h:column>
    	<f:facet name="header">
    		<h:panelGroup>
    			<h:outputText value="Artist" />
    			   <f:verbatim>[</f:verbatim>
    
    		      <h:commandLink styleClass="smallLink"
                               action="#{CDManagerBean.sort}">
    			     <h:outputText id="ascArtist" value="asc" />
    				<f:param name="by" value="artist"/>
    				<f:param name="order" value="asc"/>
    			   </h:commandLink>
    
    			<h:outputText value="," />
    					<!-- Sort descending -->
    			<h:commandLink styleClass="smallLink"
      				           action="#{CDManagerBean.sort}">
    				<h:outputText id="decArtist" value="dec" />
    				<f:param name="by" value="artist"/>
    				<f:param name="order" value="dec"/>
    			</h:commandLink>
    			     <f:verbatim>]</f:verbatim>
    		</h:panelGroup>
    	</f:facet>
    	<h:outputText value="#{cd.artist}" />
    </h:column>
    
    												
    										

    這段來(lái)自 listing.xhtml 的代碼為示例應(yīng)用程序的清單頁(yè)面生成列標(biāo)題和升序/降序排列鏈接。請(qǐng)注意,必須在多個(gè)地方重復(fù)代碼,才能輸出多列。(在上面的示例中您還會(huì)注意到,我在 ${..}#{..} 之間切換;這可能讓人迷惑,但它們做的是同樣的事!)

    所有這些渲染標(biāo)題列和藝術(shù)家列的重復(fù)代碼都破壞了 DRY 原則 —— 即,不要重復(fù)自己。那么您說(shuō),這里錯(cuò)在哪兒呢?假設(shè)在清單中平均有 5 列,應(yīng)用程序中有 20 個(gè)不同的清單。使用清單 3 的方法,就不得不重復(fù)這 35 行代碼 100 次,合計(jì) 3,500 行代碼!維護(hù)所有這些代碼會(huì)是種痛苦,但是如果決定修改清單的表示或添加一種通用的清單過(guò)濾方式,會(huì)怎么樣?需要更多、更多的工作。

    現(xiàn)在拿清單 3 和這個(gè)代碼比較:


    清單 4. 創(chuàng)建字段的新方式
    												
    														<h:dataTable id="items" value="#{CDManagerBean.cds}" var="cd"
    		rowClasses="oddRow, evenRow" headerClass="tableHeader">
    			
     <a:column entity="${cd}" fieldName="title" backingBean="${CDManagerBean}"/>
     <a:column entity="${cd}" fieldName="artist" backingBean="${CDManagerBean}"/>
    
    												
    										

    看起來(lái)好像我只用 4 行代碼就替代了 70 行或更多行代碼!可以猜出,a:column 是個(gè)復(fù)合組件。在 Facelets 中,可以容易地定義這樣的組件,如清單 5 所示:


    清單 5. column.xhtml 渲染帶有排序鏈接的列
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:z="http://www.qualcomm.com/jsf/core"
          xmlns:c="http://java.sun.com/jstl/core"
          xmlns:fn="http://java.sun.com/jsp/jstl/functions">
    
    THIS TEXT WILL BE REMOVED
    <ui:composition>
    	<!--  The label attribute is optional. Generate it if it is missing. -->
    	<c:if test="${empty label}">
    	    <c:set var="label" value="${fieldName}" />
    	</c:if>
    
    	<!--  The sort attribute is optional. Set it to true if it is missing. -->
    	<c:if test="${empty sort}">
    	    <c:set var="sort" value="${true}" />
    	</c:if>
    
    	<h:column>
    	  <f:facet name="header">
    	    <h:panelGroup>
    	      ${label} 
    	      <c:if test="${sort}">
    	          [
    	        <h:commandLink styleClass="smallLink"
    	      action="#{backingBean.sort}">
    	          <h:outputText value="asc" />
    	          <f:param name="by" 
                  value="${fieldName}"/>
    	          <f:param name="order" value="asc"/>
    	        </h:commandLink>
    					,
    	        <!-- Sort descending -->
    	        <h:commandLink styleClass="smallLink"
                  action="#{backingBean.sort}">	
    	          <h:outputText value="asc" />
    	          <f:param name="by"                
                  value="${fieldName}"/>
    	          <f:param name="order" value="dec"/>
    	        </h:commandLink>
    					]
    	      </c:if>			
    	    </h:panelGroup>
    	  </f:facet>
    	  <!--  Display the field name -->
    	  <h:outputText value="${entity[fieldName]}"/>
            </h:column>
    </ui:composition>
    THIS TEXT WILL BE REMOVED AS WELL
    </html>
    
    												
    										

    要點(diǎn)

    在進(jìn)入更高級(jí)的示例之前,我想把您的注意力引到幾件事上。首先,請(qǐng)注意在清單 5 中,我如何用一種通用方式引用值綁定:

    												
    														<h:outputText value="${entity[fieldName]}"/>
    
    												
    										

    第二,當(dāng)調(diào)用這個(gè)復(fù)合組件時(shí),我把 entityfieldName 作為屬性傳遞,如下所示:

    												
    														<a:column entity="${cd}" fieldName="title" backingBean="${CDManagerBean}"/>
    
    												
    										

    Facelets 使用的 EL 規(guī)范允許用圓點(diǎn)(.)表示法或較少使用的映射表示法引用字段。例如,如果像上面那樣調(diào)用,那么 ${entity[fieldName]} 等價(jià)于 CDManager.title。還請(qǐng)注意,我不需要 f:verbatim 標(biāo)記或輔助的 h:outputText。對(duì)于您編寫(xiě)的任何 Facelets 頁(yè)面,都可以這樣。Facelets 知道 JSF 組件樹(shù),它的唯一目的就是構(gòu)建這個(gè)組件樹(shù)。這是使用 Facelets 與使用 JSP 和 Tiles 相比的另一個(gè)優(yōu)勢(shì)。

    編寫(xiě)完成之后,就可以在其他許多地方使用 column.xhtml 復(fù)合組件。作為一個(gè)通用規(guī)則:如果正在破壞 DRY 原則,那么請(qǐng)考慮改用復(fù)合組件。







    創(chuàng)建組件

    現(xiàn)在通過(guò) column.xhtml 示例已經(jīng)快速查看了復(fù)合組件。下面一步一步地介紹創(chuàng)建復(fù)合組件的過(guò)程。以下是創(chuàng)建復(fù)合組件的步驟:

    1. 創(chuàng)建 Facelets 標(biāo)記庫(kù)。
    2. 在 web.xml 中聲明標(biāo)記庫(kù)。
    3. 用命名空間導(dǎo)入標(biāo)記文件。

    步驟 1. 創(chuàng)建 Facelets 標(biāo)記文件

    標(biāo)記文件 是符合 facelet_taglib_1_0.dtd 的文件。在概念上它與 JSP 中的 TLD 文件相似。清單 6 是一個(gè)示例標(biāo)記庫(kù)文件:


    清單 6. 標(biāo)記庫(kù)文件 —— arcmind.taglib.xml
    												
    														<?xml version="1.0"?>
    <!DOCTYPE facelet-taglib PUBLIC
      "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
      "facelet-taglib_1_0.dtd">
    <facelet-taglib>
          <namespace>http://www.arc-mind.com/jsf</namespace>
    	<tag>
    		<tag-name>field</tag-name>
    		<source>field.xhtml</source>
    	</tag>
    	<tag>
    		<tag-name>column</tag-name>
    		<source>column.xhtml</source>
    	</tag>
    	<tag>
    		<tag-name>columnCommand</tag-name>
    		<source>columnCommand.xhtml</source>
    	</tag>
    </facelet-taglib>
    
    												
    										

    arcmind.taglib.xml 文件聲明了三個(gè)標(biāo)記:fieldcolumn(已經(jīng)看過(guò)這個(gè)!)和 columnCommand。需要做的只是用 tag-name 指定標(biāo)記的名稱(chēng)和實(shí)現(xiàn)文件的位置。實(shí)現(xiàn)文件的名稱(chēng)是相對(duì)的。可以在示例 Web 應(yīng)用程序下的 WEB-INF\facelets\tags 文件中找到所有這些代碼,包括 DTD。

    請(qǐng)一定注意在上面的標(biāo)記元素之前聲明的 namespace 元素:稍后需要通過(guò)它在其他 Facelets 頁(yè)面中使用這個(gè)標(biāo)記庫(kù)。

    步驟 2. 在 web.xml 中聲明標(biāo)記庫(kù)

    有了一個(gè)標(biāo)記庫(kù)是很好,但是要讓它有用,還必須把它的存在告訴 Facelets。在 web.xml 文件中用 facelets.LIBRARIES init 參數(shù)做這件事,如下所示:

    												
    														<context-param>
    	<param-name>facelets.LIBRARIES</param-name>
    	<param-value>
    		/WEB-INF/facelets/tags/arcmind.taglib.xml
    	</param-value>
    </context-param>
    
    												
    										

    facelets.LIBRARIES 以分號(hào)分隔的列表形式傳遞,就可以想定義多少就定義多少標(biāo)記文件。

    步驟 3. 用命名空間導(dǎo)入標(biāo)記文件

    創(chuàng)建了標(biāo)記文件并在 Facelets 標(biāo)記庫(kù)中定義了它之后,就可以使用它了。標(biāo)記文件的使用要求把它聲明為 XML 命名空間,如下所示:

    												
    														<html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:a="http://www.arc-mind.com/jsf">
    
    ...
    ...
    
    <a:column entity="${cd}" fieldName="title"  
      backingBean="${CDManagerBean}"/>
    <a:column entity="${cd}" fieldName="artist" 
      backingBean="${CDManagerBean}"/>
    <a:column entity="${cd}" fieldName="price" 
      backingBean="${CDManagerBean}" sort="${false}"/>
    <a:columnCommand label="Edit" action="editCD"
    
                  backingBean="${CDManagerBean}"/>
    
    												
    										

    請(qǐng)注意命名空間的定義如下所示:

    												
    														xmlns:a="http://www.arc-mind.com/jsf"
    
    												
    										

    命名空間的值與前面步驟 1 中標(biāo)記庫(kù)中聲明的命名空間元素一樣。






    高級(jí)技巧

    上面只介紹了復(fù)合組件的基礎(chǔ)知識(shí)。用我目前為止介紹的內(nèi)容能夠創(chuàng)建可重用組件。在我自己使用 Facelets 時(shí),發(fā)現(xiàn)了一些可以讓復(fù)合組件更有用的小技巧,而且在某些情況下能夠解決一些小問(wèn)題。例如,請(qǐng)考慮來(lái)自 cdForm.xhtml 模板的以下代碼片段:


    清單 7. cdForm.xhtml 的片段
    												
    														<h:form id="cdForm">
    
      <h:inputHidden id="cdid" value="#{CDManagerBean.cd.id}" />
    
      <h:panelGrid id="formGrid" columns="3" rowClasses="row1, row2">
    
    	<!-- Title                                   -->
    	<h:outputLabel id="titleLabel" for="title" styleClass="label"
                         value="Title" />
    	<h:inputText id="title" value="#{CDManagerBean.cd.title}"
    				     required="true" />
    	<h:message id="titleMessage" for="title" styleClass="errorText"/>
    
    	<!-- Artist                                   -->
    	<h:outputLabel  id="artistLabel" for="artist" styleClass="label" 
                         value="Artist" />
    	<h:inputText id="artist" value="#{CDManagerBean.cd.artist}"
    				     required="true" />
    	<h:message id="titleMessage" for="artist"  
                         styleClass="errorText"/>
    
    	<!-- Price                                   -->
    	<h:outputLabel  id="priceLabel" for="price" styleClass="label" value="Price" />
    	<h:inputText id="price" value="#{CDManagerBean.cd.price}"
    				     required="true">
    		<f:validateDoubleRange minimum="15.0" maximum="100.0" />
    	</h:inputText>
    	<h:message id="priceMessage" for="price" styleClass="errorText"/>
    
    												
    										

    以上頁(yè)面在概念上與 清單 3 類(lèi)似,但它為 Facelets 和一個(gè)字段復(fù)合組件留出了空間,可以避免重復(fù)代碼。基于這個(gè)代碼,應(yīng)當(dāng)可以容易地創(chuàng)建顯示字段的復(fù)合組件,但是有一個(gè)小麻煩。不知道您看到?jīng)]有?請(qǐng)看表單的 price 字段:它包含一個(gè)驗(yàn)證器。

    現(xiàn)在,如何把驗(yàn)證器傳遞給復(fù)合組件?







    傳遞子元素

    這里有一個(gè)關(guān)于 Facelets 的小秘密:復(fù)合組件基本上也是一類(lèi)模板。所以,可以用 ui:define 標(biāo)記傳遞模板參數(shù),帶上具體的 ui:insert,或者也可以把體作為默認(rèn) ui:insert 傳遞。

    清單 8 就是字段組件的這樣一種實(shí)現(xiàn)(field.xhtml):


    清單 8. field.xhtml
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:z="http://www.qualcomm.com/jsf/core"
          xmlns:c="http://java.sun.com/jstl/core"
          xmlns:fn="http://java.sun.com/jsp/jstl/functions"
          xmlns:t="http://myfaces.apache.org/tomahawk">
    
    THIS TEXT WILL BE REMOVED
    <ui:composition>
    
    	      <!--  The label is optional. 
                    Generate it if it is missing. -->
    		<c:if test="${empty label}">
    			<c:set var="label" value="${fieldName}" />
    		</c:if>
    	 
    	 	<!-- The required attribute is optional, 
                     initialize it to true if not found. -->
    		<c:if test="${empty required}">
    			<c:set var="required" value="true" />
    		</c:if>
    	
    
    		<h:outputLabel id="${fieldName}Label" 
                          value="${label}" for="#{fieldName}" />			
    
    		<h:inputText id="#{fieldName}" value="#{entity[fieldName]}" 
    			             required="${required}">
    			  <ui:insert />
    		</h:inputText>
    
    		<!--  Display any error message that are found -->
    		<h:message id="${fieldName}Message" 
    			style="color: red; text-decoration: overline" 
    			for="#{fieldName}" />
    
    </ui:composition>
    THIS TEXT WILL BE REMOVED AS WELL
    
    </html>
    
    												
    										

    目前為止,清單 8 的工作基本上是不出所料。請(qǐng)注意 h:inputText 內(nèi)部未命名 ui:insert 標(biāo)記的使用。理解了它之后,就可以像清單 9 所示那樣使用這個(gè)復(fù)合組件:


    清單 9. 字段標(biāo)記復(fù)合組件
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:a="http://www.arc-mind.com/jsf">
    
    ...
    <h:form id="cdForm">
    
    	<!-- Title, Artist, Price -->
    	<a:field fieldName="title" entity="#{CDManagerBean.cd}" />
    	<a:field fieldName="artist" entity="#{CDManagerBean.cd}" />
    	<a:field fieldName="price" entity="#{CDManagerBean.cd}" >
    		<f:validateDoubleRange minimum="15.0" maximum="100.0" />
    	</a:field>
    
    ...
    
    												
    										

    price 的字段標(biāo)記被傳遞給驗(yàn)證器,作為匿名插入。因?yàn)槠渌侄螞](méi)有定義體,所以匿名插入對(duì)于默認(rèn)值沒(méi)有影響。







    傳遞動(dòng)作

    在想傳遞動(dòng)作綁定來(lái)創(chuàng)建像工具欄或?qū)Ш搅斜磉@樣的元素時(shí),問(wèn)題就是使用標(biāo)準(zhǔn)的表達(dá)式語(yǔ)言,不能傳遞,但是有方法可以做到!使用從對(duì)象中引用字段的相同方式,可以引用對(duì)象中的方法。所以,要?jiǎng)?chuàng)建可以創(chuàng)建動(dòng)作綁定的組件,可以像下面這樣做(來(lái)自 columnCommand.xhtml):

    												
    														<h:commandLink id="#{action}" value="#{label}"  
                                  action="#{backingBean[action]}"/>
    
    
    												
    										

    請(qǐng)研究動(dòng)作屬性的 value。請(qǐng)注意,我使用與前面從實(shí)體引用字段相同的方式,訪問(wèn)到了方法。可以用以下語(yǔ)法調(diào)用這個(gè)組件:

    												
    														<a:columnCommand label="Edit" action="editCD" 
    backingBean="${CDManagerBean}"/>
    
    												
    										

    這個(gè)調(diào)用把 CDManagerBeaneditCD() 方法綁定到鏈接。清單 10 顯示了 columnCommand.xhtml 的完整清單:


    清單 10. columnCommand.xhtml
    												
    														<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:z="http://www.qualcomm.com/jsf/core"
          xmlns:c="http://java.sun.com/jstl/core">
    
    THIS TEXT WILL BE REMOVED
    <ui:composition>
    
    	<!--  The label is optional. Generate it if it is missing. -->
    	<c:if test="${empty label}">
    			<c:set var="label" value="${action}" />
    	</c:if>
    	
    	<h:column>
    		<f:facet name="header">
    			<h:panelGroup>
    				<h:outputText value="#{label}" />
    			</h:panelGroup>
    		</f:facet>
    		<h:commandLink id="#{action}" value="#{label}" 
                                         action="#{backingBean[action]}"/>
    	</h:column>
    		
    </ui:composition>
    THIS TEXT WILL BE REMOVED AS WELL
    
    </html>
    
    												
    										







    Facelets 的不足

    我已經(jīng)清楚地演示了使用 Facelets 的好處:即組件復(fù)合和模板框架,它的核心 是組件,而不是 Servlets 輸出。但是采用 Facelets 也有些不足。其中之一就是,對(duì) Facelets 的 IDE 支持極少。只有一個(gè) Eclipse IDE 實(shí)現(xiàn)完全支持 Facelets(商業(yè)版的實(shí)現(xiàn),請(qǐng)參閱 參考資料),而且看起來(lái)還不支持代碼補(bǔ)足。

    而且也沒(méi)對(duì) Facelets 調(diào)試的 IDE 支持(也就是說(shuō),設(shè)置斷點(diǎn)之類(lèi)的東西)。要想調(diào)試,需要閱讀 Facelets 手冊(cè),打開(kāi) JDK 1.4 樣式的日志,根據(jù)開(kāi)發(fā)情況設(shè)置它的 init 參數(shù)。

    在有利方面來(lái)說(shuō),我發(fā)現(xiàn)使用 Facelets API 非常自然和直觀。調(diào)試在開(kāi)始的時(shí)候有些怪異,但是后來(lái)就適應(yīng)了。隨 Facelets 發(fā)行包提供的演示應(yīng)用程序沒(méi)有定制標(biāo)記或功能的示例,但是核心項(xiàng)目代碼中有,所以請(qǐng)用核心項(xiàng)目代碼作為指導(dǎo)。

    如果要使用新的 JSF 組件庫(kù),必須有公開(kāi)這個(gè)庫(kù)的 Facelets 標(biāo)記庫(kù)。有些主要的組件庫(kù)(例如 Oracle 和 Tomahawk)的標(biāo)記庫(kù)存在,但是即使這些也需要調(diào)整。我必須調(diào)整 Tomahawk 標(biāo)記庫(kù)才能在應(yīng)用程序中得到 Tomahawk 的日歷組件。雖然編寫(xiě)導(dǎo)出組件的標(biāo)記庫(kù)文件比較容易,但是也是件麻煩事。如果想使用新的定制組件庫(kù),就必須編寫(xiě)標(biāo)記庫(kù)文件。

    因?yàn)樵谄渌麑?shí)現(xiàn)中的問(wèn)題,F(xiàn)acelets 看起來(lái)只能用于 MyFaces 1.1.1 和 Sun 1.2 JSF 的參考實(shí)現(xiàn)(Sun 的 JSF RI 1.2 還沒(méi)有正式發(fā)布)。不能把 Facelets 用于 1.1 RI。雖然可以把 MyFaces 用于 IBM WebSphere,但不能把 Facelets 用于 IBM 的實(shí)現(xiàn)。(如果使用最新版本的 Facelets,必須使用最新構(gòu)建的 MyFaces 1.1.2,它現(xiàn)在還沒(méi)推出。)

    還要注意的是,MyFaces 1.1 和 JSF RI 1.2 的底層機(jī)制不同。盡管如此,F(xiàn)acelets 試圖把這兩者的實(shí)現(xiàn)保持為它們當(dāng)前的形式(MyFaces 1.1.2 和 JSF RI 1.2),這看起來(lái)解釋了花在 Facelets 上的大量時(shí)間。如果雙方更團(tuán)結(jié)協(xié)調(diào)一點(diǎn),讓 Facelets 在兩個(gè)環(huán)境中做同樣的事上少花些時(shí)間,就可以把更多時(shí)間花在改進(jìn) Facelets 上。







    結(jié)束語(yǔ)

    雖然有些缺點(diǎn),我還是強(qiáng)烈推薦您下載 Facelets 并盡快開(kāi)始使用它。Facelets 是 JSF 的未來(lái),或者將會(huì)是,使用它可以使您在任何 JSF 暴風(fēng)雨中都干爽(DRY 的雙關(guān)語(yǔ),譯者注)。如果還不能在當(dāng)前項(xiàng)目中使用它,請(qǐng)?jiān)谙乱粋€(gè)項(xiàng)目中想著它。

    我已經(jīng)用 Facelets 創(chuàng)建了復(fù)合組件、定制 Facelet 標(biāo)記和內(nèi)部 CRUD 框架功能的整個(gè)庫(kù)。我發(fā)現(xiàn)了許多構(gòu)建復(fù)合組件的技巧和技術(shù)(例如,自動(dòng)生成復(fù)選框、日歷組件的字段標(biāo)記,或者根據(jù)綁定到組件的值綁定類(lèi)型的文本字段),超過(guò)了在這樣一篇介紹性文章中能涉及的內(nèi)容。相反,我把重點(diǎn)放在讓您了解和運(yùn)行復(fù)合組件。通過(guò)在這里學(xué)到的知識(shí),只用最少的定制功能和定制 Facelets 標(biāo)記,就可以創(chuàng)建動(dòng)人的組件。

    致謝

    特別感謝 Jacob Hookom,他是 Facelets 的創(chuàng)造者,感謝他對(duì)本文的審閱和貢獻(xiàn),還要感謝 Athenz 細(xì)致入微的編輯。








    下載

    描述 名字 大小 下載方法
    Facelets source code j-facelets_code.zip 267KB HTTP
    Facelets source code with jars and wars j-faceletsjarsandwars.zip 47MB HTTP

    參考資料

    學(xué)習(xí)
    主站蜘蛛池模板: 国产亚洲精品美女久久久久| 亚洲精品乱码久久久久久| 麻豆最新国产剧情AV原创免费 | 亚洲卡一卡2卡三卡4麻豆| 亚洲视频在线观看视频| 久久精品a亚洲国产v高清不卡| 亚洲电影一区二区三区| 亚洲老熟女@TubeumTV| 亚洲天堂中文字幕在线观看| 亚洲国产高清视频在线观看| 亚洲一区二区三区91| 2020久久精品亚洲热综合一本| 精品国产日韩久久亚洲| 亚洲av中文无码乱人伦在线观看 | 久久国产亚洲观看| 亚洲日本在线看片| 亚洲免费中文字幕| 亚洲色中文字幕在线播放| 亚洲av无码专区在线电影| 无遮挡免费一区二区三区| 中国国语毛片免费观看视频| 国产免费阿v精品视频网址| 人妻丰满熟妇无码区免费| 成人免费在线看片| 日韩免费视频观看| 最新国产AV无码专区亚洲| 久久夜色精品国产亚洲AV动态图| 亚洲另类视频在线观看| 色欲aⅴ亚洲情无码AV| 一区免费在线观看| 久久九九AV免费精品| 两性刺激生活片免费视频| 又爽又黄无遮挡高清免费视频| 伊人久久亚洲综合| 亚洲综合激情六月婷婷在线观看 | 亚洲精品二区国产综合野狼 | 亚洲av日韩片在线观看| 亚洲va久久久噜噜噜久久狠狠| 亚洲国产精品综合久久网各| jzzijzzij在线观看亚洲熟妇| 国产人成网在线播放VA免费|