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

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

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

    happyfish

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      38 Posts :: 1 Stories :: 62 Comments :: 0 Trackbacks
    1. 介紹
    1.1 Model-View-Controller (MVC) 設(shè)計模式
    FIXME - 需要一個對該模式一般性的介紹。(譯注:可以參考機械工業(yè)出版社的《設(shè)計模式》。) 
    1.2 將MVC概念映射到Struts組件中
    Struts 的體系結(jié)構(gòu)實現(xiàn)了Model-View-Controller設(shè)計模式的概念,它將這些概念映射到web應(yīng)用程序的組件和概念中,如下圖所示: 

    --------------------------------------------------------------------------------



    --------------------------------------------------------------------------------

    這一體系結(jié)構(gòu)中每個主要的組件都將在下面做詳細的討論。 

    1.3 Model: 系統(tǒng)狀態(tài)和商業(yè)邏輯JavaBeans
    基于MVC的系統(tǒng)中的 Model 部分可以細分為兩個概念 -- 系統(tǒng)的內(nèi)部狀態(tài), 能夠改變狀態(tài)的行為。用語法術(shù)語來說,我們可以把狀態(tài)信息當作名詞(事物),把行為當作動詞(事物狀態(tài)的改變)。 
    通常說來,你的應(yīng)用程序?qū)⑾到y(tǒng)內(nèi)部的狀態(tài)表示為一組一個或多個的JavaBeans,使用屬性(properties)來表示狀態(tài)的細節(jié)。依賴于你的應(yīng)用程序的復(fù)雜度,這些beans可以是自包含的(以某種方式知道怎樣永久地保存它們的狀態(tài)信息),或者可以是正面的(facades),知道當被請求時怎樣從外部數(shù)據(jù)源(例如數(shù)據(jù)庫)中取得信息。Entity EJBs通常也用來表示內(nèi)部狀態(tài)。 

    大型應(yīng)用程序經(jīng)常將系統(tǒng)可能的商業(yè)邏輯行為表示為可以被維護狀態(tài)信息的beans調(diào)用的方法。舉個例子,你有一個為每個當前用戶保存在session中的購物車bean,里面是表示當前用戶決定購買物品的屬性。這個bean有一個checkOut()方法用來驗證用戶的信用卡,將定單發(fā)給庫房以選擇貨品和出貨。別的系統(tǒng)分別地表示同樣的行為,或許使用Session EJBs。 

    在一些小型應(yīng)用程序中,同樣的行為又可能嵌入到作為Controller一部分的 Action 類中。這在邏輯非常簡單或者并不想要在其它環(huán)境中重用這些商業(yè)邏輯時是恰當?shù)摹truts框架支持所有這些方法,但建議將商業(yè)邏輯(“做什么”)和 Action 類(“決定做什么”)分離開。 

    1.4 View: JSP頁面和表示組件
    基于Struts的應(yīng)用程序中的 View 部分通常使用JSP技術(shù)來構(gòu)建。JSP頁面包含稱為“模版文本”的靜態(tài)HTML(或XML)文本,加上插入的基于對特殊行為標記解釋的動態(tài)內(nèi)容。JSP環(huán)境包括了其用途由JSP規(guī)范來描述的一套標準的行為標記,例如 <jsp:useBean> 。另外,還有一個用來定義你自己標記的標準機制,這些自定義的標記組織在“定制標記庫”中。 
    Struts包括了一個廣闊的便于創(chuàng)建用戶界面,并且充分國際化的定制標記庫,與作為系統(tǒng) Model 部分一部分的ActionForm beans美妙地相互配合。這些標記的使用將在后面做詳細討論。 

    除了JSP頁面和其包含的行為及定制標記,商業(yè)對象經(jīng)常需要能夠基于它們在被請求時的當前狀態(tài)將自己處理成HTML(或XML)。從這些對象處理過的輸出可以很容易地使用 <jsp:include> 標準行為標記包括在結(jié)果的JSP頁面中。 

    1.5 Controller: ActionServlet和ActionMapping
    應(yīng)用程序的 Controller 部分集中于從客戶端接收請求(典型情況下是一個運行瀏覽器的用戶),決定執(zhí)行什么商業(yè)邏輯功能,然后將產(chǎn)生下一步用戶界面的責任委派給一個適當?shù)腣iew組件。在Struts中,controller的基本組件是一個 ActionServlet 類的servlet。這個servlet通過定義一組映射(由Java接口 ActionMapping 描述)來配置。每個映射定義一個與所請求的URI相匹配的路徑和一個 Action 類(一個實現(xiàn) Action 接口的類)完整的類名,這個類負責執(zhí)行預(yù)期的商業(yè)邏輯,然后將控制分派給適當?shù)腣iew組件來創(chuàng)建響應(yīng)。 
    Struts也支持使用包含有運行框架所必需的標準屬性之外的附加屬性的 ActionMapping 類的能力。這允許你保存特定于你的應(yīng)用程序的附加信息,同時仍可利用框架其余的特性。另外,Struts允許你定義控制將重定向到的邏輯名,這樣一個行為方法可以請求“主菜單”頁面(舉例),而不需要知道相應(yīng)的JSP頁面的實際名字是什么。這個功能極大地幫助你分離控制邏輯(下一步做什么)和顯示邏輯(相應(yīng)的頁面的名稱是什么)。 

    2. 創(chuàng)建Model組件
    2.1 概述
    你用到的應(yīng)用程序的需求文檔很可能集中于創(chuàng)建用戶界面。然而你應(yīng)該保證每個提交的請求所需要的處理也要被清楚的定義。通常說來,Model 組件的開發(fā)者集中于創(chuàng)建支持所有功能需求的JavaBeans類。一個特殊應(yīng)用要求的beans的精確特性依賴于具體需求變化會非常的大,但是它們通常可以分成下面討論的幾種類型。然而,首先對“范圍”概念做一個簡短的回顧是有用的,因為它與beans有關(guān)。 
    2.2 JavaBeans和范圍
    在一個基于web的應(yīng)用程序中,JavaBeans可以被保存在(并從中訪問)一些不同“屬性”的集合中。每一個集合都有集合生存期和所保存的beans可見度的不同的規(guī)則。總的說來,定義生存期和可見度的這些規(guī)則被叫做這些beans的 范圍 。JSP規(guī)范中使用以下術(shù)語定義可選的范圍(在圓括號中定義servlet API中的等價物): 
    page - 在一個單獨的JSP頁面中可見的Beans,生存期限于當前請求。(service()方法中的局部變量) request - 在一個單獨的JSP頁面中可見的Beans,也包括所有包含于這個頁面或從這個頁面重定向到的頁面或servlet。(Request屬性) 
    session - 參與一個特定的用戶session的所有的JSP和servlet都可見的Beans,跨越一個或多個請求。(Session屬性) 
    application - 一個web應(yīng)用程序的所有JSP頁面和servlet都可見的Beans。(Servlet context屬性) 
    記住同一個web應(yīng)用程序的JSP頁面和servlets共享同樣一組bean集合是很重要的。例如,一個bean作為一個request屬性保存在一個servlet中,就象這樣: 
    MyCart mycart = new MyCart(...);
    request.setAttribute("cart", mycart);
    將立即被這個servlet重定向到的一個JSP頁面使用一個標準的行為標記看到,就象這樣: 
    <jsp:useBean id="cart" scope="request"
    class="com.mycompany.MyApp.MyCart"/>
    2.3 ActionForm Beans
    Struts框架通常假定你已經(jīng)為每一個你的應(yīng)用程序中請求的輸入創(chuàng)建了一個 ActionForm bean(即一個實現(xiàn)了 ActionForm 接口的類)。如果你在你的 ActionMapping 配置文件中定義了這樣的beans(見“創(chuàng)建Controller組件”),Struts的controller servlet在調(diào)用適當?shù)?nbsp;Action 方法前將自動為你執(zhí)行如下的服務(wù): 
    用適當?shù)年P(guān)鍵字檢查用戶的session中是否有適當?shù)念惖腷ean的一個實例。 
    如果沒有這樣的session范圍的bean,自動建立一個新的bean并添加到用戶的session中。 
    對每個名字對應(yīng)于bean中的一個屬性的請求參數(shù),調(diào)用相應(yīng)的set方法。這個操作類似于當你以通配符“*”選擇所有屬性使用標準的JSP行為標記 <jsp:setProperty> 。 
    更新的ActionForm bean在被調(diào)用時將被傳遞給Acton類的perform()方法,以使這些值能夠立即生效。 
    當你在寫你的ActionForm beans時,記住以下的原則: 
    ActionForm 接口本身不需要特殊的實現(xiàn)方法。它是用來標識這些特定的beans在整個體系結(jié)構(gòu)中的作用。典型情況下,一個ActionForm bean只包括屬性的get方法和set方法,沒有商業(yè)邏輯。 
    通常在一個ActionForm bean中只有很少的輸入驗證邏輯。這樣的beans存在的主要理由是保存用戶為相關(guān)的表單所輸入的大部分近期值 -- 甚至在錯誤被檢測到時 -- 這樣同樣的頁面可以被重建,伴隨有一組出錯信息,這樣用戶僅僅需要糾正錯誤的字段。用戶輸入的驗證應(yīng)該在 Action 類中執(zhí)行(如果是很簡單的話),或者在適當?shù)纳虡I(yè)邏輯beans中執(zhí)行。 
    為每個表單中出現(xiàn)的字段定義一個屬性(用相關(guān)的getXxx()和setXxx()方法)。字段名和屬性名必須按照JavaBeans的約定相匹配。例如,一個名為 username 的輸入字段將引起 setUsername() 方法被調(diào)用。 
    你應(yīng)該注意一個“表單”在這里討論時的意義并不必須對應(yīng)于用戶界面中的一個單獨的JSP頁面。在很多應(yīng)用程序中一個“表單”(從用戶的觀點)延伸至多個頁面也是很平常的。想想看,例如,通常在安裝新的應(yīng)用程序時使用的導(dǎo)航安裝程序的用戶界面。Struts鼓勵你定義一個包含所有字段屬性的單獨的ActionForm bean。不管字段實際上是顯示在哪個頁面上。同樣的,同一表單的不同的頁面應(yīng)該提交到相同的Action類。如果你遵照這個建議,在大多數(shù)情況下,頁面設(shè)計者可以重新組織不同頁面中的字段而不需要改變處理邏輯。 
    2.4 系統(tǒng)狀態(tài)Beans
    系統(tǒng)的實際狀態(tài)通常表示為一組一個或多個的JavaBeans類,其屬性定義當前狀態(tài)。例如,一個購物車系統(tǒng)包括一個表示購物車的bean,這個bean為每個單獨的購物者維護,這個bean中包括(在其它事物之中)一組購物者當前選擇購買的項目。分別地,系統(tǒng)也包括保存用戶信息(包括他們的信用卡和送貨地址)、可獲得項目的目錄和它們當前庫存水平的不同的beans。 
    對于小規(guī)模的系統(tǒng),或者對于不需要長時間保存的狀態(tài)信息,一組系統(tǒng)狀態(tài)beans可以包含所有系統(tǒng)曾經(jīng)經(jīng)歷的特定細節(jié)的信息。或者經(jīng)常是,系統(tǒng)狀態(tài)beans表示永久保存在一些外部數(shù)據(jù)庫中的信息(例如CustomerBean對象對應(yīng)于表 CUSTOMERS 中的特定的一行),在需要時從服務(wù)器的內(nèi)存中創(chuàng)建或清除。在大規(guī)模應(yīng)用程序中,Entity EJBs也用于這種用途。 

    2.5 商業(yè)邏輯Beans
    你應(yīng)該把你的應(yīng)用程序中的功能邏輯封裝成對為此目的設(shè)計的JavaBeans的方法調(diào)用。這些方法可以是用于系統(tǒng)狀態(tài)beans的相同的類的一部分,或者可以是在專門執(zhí)行商業(yè)邏輯的獨立的類中。在后一種情況下,你通常需要將系統(tǒng)狀態(tài)beans傳遞給這些方法作為參數(shù)處理。 
    為了代碼最大的可重用性,商業(yè)邏輯beans應(yīng)該被設(shè)計和實現(xiàn)為它們不知道自己被執(zhí)行于web應(yīng)用環(huán)境中。如果你發(fā)現(xiàn)在你的bean中你必須import一個 javax.servlet.* 類,你就把這個商業(yè)邏輯捆綁在了web應(yīng)用環(huán)境中。考慮重新組織事物使你的 Action 類(Controller任務(wù)的一部分,在下面描述)翻譯所有從HTTP請求中請求被處理為對你的商業(yè)邏輯beans屬性set方法調(diào)用的信息,然后可以發(fā)出一個對 execute() 的調(diào)用。這樣的一個商業(yè)邏輯類可以被重用在除它們最初被構(gòu)造的web應(yīng)用程序以外的環(huán)境中。 

    依賴于你的應(yīng)用程序的復(fù)雜度和范圍,商業(yè)邏輯beans可以是與作為參數(shù)傳遞的系統(tǒng)狀態(tài)beans交互作用的普通的JavaBeans,或者使用JDBC調(diào)用訪問數(shù)據(jù)庫的普通的JavaBeans。而對于較大的應(yīng)用程序,這些beans經(jīng)常是有狀態(tài)或無狀態(tài)的EJBs。 

    2.6 題外話: 訪問關(guān)系數(shù)據(jù)庫
    很多web應(yīng)用程序利用一個關(guān)系數(shù)據(jù)庫(通過一個JDBC driver訪問)來保存應(yīng)用程序相關(guān)的永久數(shù)據(jù)。其它應(yīng)用程序則使用Entity EJBs來實現(xiàn)這個目的,他們委派EJBs自己來決定怎樣維護永久狀態(tài)。如果你是使用EJBs來實現(xiàn)這個目的,遵照EJB規(guī)范中描述的客戶端設(shè)計模式。 
    對于基于直接數(shù)據(jù)庫訪問的web應(yīng)用程序,一個普通的設(shè)計問題是當需要訪問低層數(shù)據(jù)庫時怎樣產(chǎn)生一個適當?shù)腏DBC連接對象。解決這個問題有幾種方法 -- 以下原則描述了推薦的一種方法: 

    創(chuàng)建或得到一個允許一組數(shù)據(jù)庫連接被多個用戶共享的ConnectionPool類。Struts(當前)沒有包括這樣的一個類,但是有很多這樣的類可以得到。 
    當應(yīng)用程序初始化時,在應(yīng)用程序展開(deployment)描述符中定義一個有一個“啟動時加載”值的servlet。我們將把這個servlet叫做 啟動 servlet。在大多數(shù)情況下,這個servlet不需要處理任何的請求,所以沒有一個 <servlet-mapping> 會指向它。 
    在啟動servlet的 init() 方法中,配置并初始化一個ConnectionPool類的實例,將其保存為一個servlet context屬性(從JSP的觀點看等同于一個application范圍的bean)。通常基于傳遞給啟動servlet初始化參數(shù)來配置聯(lián)接緩沖池是很方便的。 
    在啟動servlet的 destroy() 方法中,包含了釋放聯(lián)接緩沖池所打開的聯(lián)接的邏輯。這個方法將在servlet容器結(jié)束這個應(yīng)用程序的時候被調(diào)用。 
    當 Action 類需要調(diào)用一個需要數(shù)據(jù)庫聯(lián)接的商業(yè)邏輯bean中的方法(例如“insert a new customer”)時,將執(zhí)行下面的步驟: 
    為這個web應(yīng)用程序從servelt context屬性中得到一個聯(lián)接緩沖池對象。 
    調(diào)用聯(lián)接緩沖池對象的 open() 方法來得到一個在 Action 類調(diào)用中使用的聯(lián)接。 
    調(diào)用商業(yè)邏輯bean中合適的方法,將數(shù)據(jù)庫聯(lián)接對象作為一個參數(shù)傳遞給它。 
    調(diào)用分配的聯(lián)接中的 close() 方法,這將引起這個聯(lián)接為了以后其它請求的重用被返回到緩沖池中。 
    一個通常的編程錯誤是忘記了把數(shù)據(jù)庫聯(lián)接返回給緩沖池,這將最終導(dǎo)致用完所有的聯(lián)接。一定要確信 Action 類的邏輯總是返回聯(lián)接,甚至在商業(yè)邏輯bean拋出一個違例時。 
    遵照上面推薦的設(shè)計模式意味著你能夠編寫你的商業(yè)邏輯類而不需要擔心它們怎樣得到一個JDBC聯(lián)接來使用-- 簡單地在任何需要訪問數(shù)據(jù)庫的方法中包含一個Connection參數(shù)。當你的商業(yè)邏輯類在一個web應(yīng)用程序中被利用時,分配和釋放適當?shù)穆?lián)接是 Action 類的責任。當你使用相同的商業(yè)邏輯類時,例如,在一個批處理工作中,提供一個適當?shù)穆?lián)接是那個應(yīng)用程序的責任(這不需要從緩沖池中獲得,因為大多數(shù)批處理工作運行于一個單線程環(huán)境中)。 
    3. 創(chuàng)建View組件
    3.1 概述
    這一章集中于創(chuàng)建應(yīng)用程序中的 View 組件的任務(wù),主要使用JSP技術(shù)建立。特別的,Struts除了提供了與輸入表單的交互外還提供了建立國際化應(yīng)用程序的支持。幾個其它的與View相關(guān)的主題也被簡單地討論。 
    3.2 國際化消息
    幾年之前,應(yīng)用程序開發(fā)者能夠考慮到僅僅支持他們本國的只使用一種語言(或者有時候是兩種)和通常只有一種數(shù)量表現(xiàn)方式(例如日期、數(shù)字、貨幣值)的居民。然而,基于web技術(shù)的應(yīng)用程序的爆炸性增長,以及將這些應(yīng)用程序展開在Internet或其它被廣泛訪問的網(wǎng)絡(luò)之上,已經(jīng)在很多情況下使得國家的邊界淡化到不可見。這種情況轉(zhuǎn)變成為一種對于應(yīng)用程序支持國際化(經(jīng)常被稱做“i18n”,因為18是字母“i”和字母“n”之間的字母個數(shù))和本地化的需求。 
    Struts建立于Java平臺之上為建立國際化和本地化的應(yīng)用程序提供幫助。需要熟悉的關(guān)鍵概念是: 

    Locale - 基礎(chǔ)的支持國際化的Java類是 java.util.Locale 。每個 Locale 代表一個特別的國家和語言選擇(加上一個可選的語言變量),以及一套格式假定,例如數(shù)字和日期等等。 
    ResourceBundle - java.util.ResourceBundle 類提供支持多種語言消息的基本工具。查看文檔中關(guān)于ResourceBundle 類以及你的JDK版本的文檔包中關(guān)于國際化的更多內(nèi)容。 
    PropertyResourceBundle - 一個 ResourceBundle 類的標準實現(xiàn)允許你使用與初始化properties文件同樣的“name=value”語法來定義資源。這對于使用為用于一個web應(yīng)用程序的消息準備資源包是非常方便的,因為這些消息通常都是面向文本的。 
    MessageFormat - java.text.MessageFormat 類允許你使用運行時指定的參數(shù)替換一個消息字符串中的一部分(在這種情況下,是一個從一個資源包得到的消息)。這在你創(chuàng)建一個句子的場合中是有用的,但是詞會以不同的語言按照不同的順序出現(xiàn)。消息中的占位符字符串{0}用第一個運行時參數(shù)替換,{1}用第二個運行時參數(shù)替換,以此類推。 
    MessageResources - Struts的類 org.apache.struts.util.MessageResources 使你能夠?qū)⒁惶踪Y源包視做一個數(shù)據(jù)庫,并且允許你為一個特定的Locale(通常是與當前用戶相對應(yīng))請求一個特定的消息,而不是為服務(wù)器運行在其中的缺省的Locale請求消息。 
    對于一個國際化的應(yīng)用程序,遵照JDK文檔包中國際化文檔所描述的步驟來創(chuàng)建一個包含每種語言的消息的屬性文件。下面舉一個例子說明。 
    假設(shè)你的源代碼建立在包 com.mycompany.mypackage 中,因此它保存于一個叫做(相對于你的源目錄)com/mycompany/mypackage 的目錄中。為創(chuàng)建一個叫做 com.mycompany.mypackage.MyResources 的資源包,你應(yīng)該在目錄 com/mycompany/mypackage 中創(chuàng)建下列文件: 

    MyResources.properties - 包含你的服務(wù)器的缺省語言的消息。如果你的缺省語言是英語,你可能有一個這樣的條目: 
    prompt.hello=Hello
    MyResources_xx.properties - 包含ISO語言編程為“xx”(查看ResourceBundle的Java文檔頁面得到一個當前列表的聯(lián)接)的同樣的消息。對于上面的消息的法語版,你可以有這個條目: 
    prompt.hello=Bonjour
    你可以有你需要的任意多的語言的資源包文件。
    當你在web應(yīng)用程序展開描述符中配置controller servlet時,你需要在一個初始化參數(shù)中定義的一件事是應(yīng)用程序的資源包的基礎(chǔ)名。在上述的情況中,這應(yīng)該是 com.mycompany.mypackage.MyResources 。 
    3.3 表單和FormBean的交互
    大部分web開發(fā)者曾經(jīng)使用HTML的標準性能來建立表單,例如使用 <input> 標記。用戶希望交互程序具有一定的行為,這些期待中的一個與錯誤處理有關(guān) -- 如果用戶出現(xiàn)一個錯誤,應(yīng)用程序應(yīng)該允許他們僅僅修改需要修改的部分 -- 而不需要重新敲入當前頁面或表單中的任何其它信息。 
    使用標準的HTML和JSP編程來完全實現(xiàn)這個期望是單調(diào)而繁重的。舉例來說,一個用戶名字段的輸入元素看起來可以象是這樣(在JSP中) 

    <input type="text" name="username"
    value="<%= loginBean.getUsername() %>">
    這很難敲對,會把沒有編程概念的HTML開發(fā)者搞糊涂,并且會在HTML編輯器中造成問題。取而代之的是,Struts提供了一種全面的基于JSP 1.1的定制標記庫功能的機制來建立表單。上面的情況使用Struts處理后將象是這樣: 
    <struts:text name="username"/>
    沒有必要再顯式地涉及到從中獲得初始值的JavaBean。這將由框架自動處理。 
    3.3.1 使用Struts建立表單
    一個完整的注冊表單將演示Struts相對于直接使用HTML和標準的JSP功能怎樣極大地減輕了處理表單的痛苦。考慮以下稱為logon.jsp的頁面(來自Struts的例子程序): 
    <%@ page language="java" %>
    <%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>

    <html>
    <head>
    <title><struts:message key="logon.title"/></title>
    <body bgcolor="white">

    <struts:errors/>

    <struts:form action="logon.do" name="logonForm"
    type="org.apache.struts.example.LogonForm"/>
    <table border="0" width="100%">
    <tr>
    <th align="right">
    <struts:message key="prompt.username"/>
    </th>
    <td align="left">
    <struts:text name="username" size="16"/>
    </td>
    </tr>
    <tr>
    <th align="right">
    <struts:message key="prompt.password"/>
    </th>
    <td align="left">
    <struts:password name="password" size="16"/>
    </td>
    </tr>
    <tr>
    <td align="right">
    <struts:submit>
    <struts:message key="button.submit"/>
    </struts:submit>
    </td>
    <td align="right">
    <struts:reset>
    <struts:message key="button.reset"/>
    </struts:reset>
    </td>
    </tr>
    </table>
    </struts:form>

    </body>
    </html>
    下面的條目基于這個例子演示在Struts中處理表單的關(guān)鍵的特性: 
    taglib指令告訴JSP頁面編譯器從哪里找到Struts標記庫的 標記庫描述符 。在這種情況下,我們使用struts作為前綴來標識來自這個庫中的標記,但是可以使用任何你想用的前綴。 
    這個頁面使用了幾個 message 標記來從一個包含有這個應(yīng)用程序所有資源的 MessageResources 對象中查找國際化的消息字符串。為了讓這個頁面能夠工作,以下的消息關(guān)鍵字必須在這些資源中被定義: 
    logon.title - 注冊頁面的標題 
    prompt.username - 一個 “Username:” 提示字符串 
    prompt.password - 一個 “Password:” 提示字符串 
    button.submit - “Submit”按鈕的標簽 
    button.reset - “Reset”按鈕的標簽 
    當用戶注冊時,應(yīng)用程序可以在用戶的session中保存一個 Locale 對象。這個 Locale 將用來選擇適當語言的消息。這使得給用戶一個切換語言的可選項實現(xiàn)起來變的容易了 -- 僅僅改變保存的 Locale 對象,所有的消息就會自動切換。 
    errors 標記顯示由一個商業(yè)邏輯組件保存的任何出錯消息,或者如果沒有出錯消息保存就什么都沒有。這個標記將在下面做深入的描述。 
    form 標記基于指定的屬性對一個HTML <form> 元素進行處理。它也將所有在這個表單中的字段與一個保存在關(guān)鍵字 logonForm 下的session范圍的FormBean相關(guān)聯(lián)。這個bean用來為所有的具有與bean中的屬性名匹配的名字的輸入字段提供初始值。如果適當?shù)腷ean沒有找到,一個新的bean將會被自動建立,使用指定的Java類名。 
    text 標記對一個類型為“text”的HTML <input> 元素進行處理。在這種情況下,占據(jù)瀏覽器屏幕的字符位置的數(shù)字也被指定。當頁面被執(zhí)行時,是相對應(yīng)的bean的 username 屬性的當前值(也就是 getUsername() 的返回值)。 
    password 標記使用方法類似。不同之處是當用戶敲入他們的口令時瀏覽器將回應(yīng)星號字符,而不是輸入值。 
    submit 和 reset 標記在表單低部生成相應(yīng)的按鈕。每個按鈕的文本標簽使用 message 標記建立,同時帶有提示,這樣這些值就是國際化的。 
    3.3.2 輸入字段類型支持
    Struts為所有以下類型的輸入字段定義了標記,帶有與其相應(yīng)的參考信息的超聯(lián)接。 
    checkboxes 
    hidden 字段 
    password 輸入字段 
    radio 按鈕 
    reset 按鈕 
    select 列表和嵌入的 
    options 
    submit 按鈕 
    text 輸入字段 
    textareas 
    在所有情況下,一個字段標記都必須嵌套在一個 form 標記中,這樣字段才知道使用哪個bean來初始化顯示的值。 
    3.3.3 其它有用的表示標記
    在Struts的標記庫中有幾個其它的標記對于建立用戶界面是有幫助的: 
    enumerate 為一個指定集合的每個元素重復(fù)一次標記體(可以是一個Enumeration,一個Hashtable,一個Vector或一個對象數(shù)組)。 
    getProperty 從指定的bean中得到指定的屬性,并且在本頁面的其余部分作為一個page范圍的bean存在。這是訪問一個被 enumerate 使用的集合的方便的方法。 
    ifAttributeExists 只有在一個指定的屬性存在于一個指定的范圍中時才對標記體求值。 
    ifAttributeMissing 只有在一個指定的屬性不存在于一個指定的范圍中時才對標記體求值。 
    ifParameterEquals 只有在一個指定的請求參數(shù)具有一個指定的值時才對標記體求值。 
    ifParameterNotEquals 只有在一個指定的請求參數(shù)不具有一個指定的值或者不存在時才對標記體求值。 
    ifParameterNotNull 只有在一個指定的請求參數(shù)包含在這個請求中并且長度大于0時才對標記體求值。 
    ifParameterNull 只有在一個指定的請求參數(shù)不包含在這個請求中或者長度等于0時才對標記體求值。 
    iterate 為一個指定集合中的每個元素重復(fù)一次標記體(可以是一個Collection,一個Iterator,一個Map,或者一個對象數(shù)組)。這個標記在Java2環(huán)境中代替了 enumerate 標記。 
    link 生成一個超聯(lián)接,當沒有cookie支持時自動應(yīng)用URL編程來維護session狀態(tài)。 
    parameter 處理指定請求參數(shù)的值,適當?shù)剡^濾HTML中有特殊含義的字符。 
    property 顯示一個表單中命名的bean屬性 -- 在屬性應(yīng)該是只讀時使用這個標記而不是 text 標記。 
    3.3.4 自動表單驗證
    除了上面描述的表單和bean的交互外,如果你的bean知道怎樣驗證它接收的輸入字段,Struts還提供一種附加的機制。為了利用這個特性,使你的bean類實現(xiàn) ValidatingActionForm 接口,而不是 ActionForm 接口。一個 ValidatingActionForm 增加了一個附加的方法簽名: 
    public String[] validate()
    對于一個被controller servlet在bean屬性已經(jīng)組裝但是在相應(yīng)的行為類的 perform() 方法被調(diào)用之前調(diào)用的方法,validate() 方法有以下可選項: 
    執(zhí)行適當?shù)尿炞C發(fā)現(xiàn)沒有錯誤 -- 返回 null 或者一個非0長度字符串數(shù)組,并且controller servlet將繼續(xù)調(diào)用適當?shù)?nbsp;Action 類的 perform() 方法。 
    執(zhí)行適當?shù)尿炞C發(fā)現(xiàn)有錯誤 -- 返回一個內(nèi)容為應(yīng)該被顯示的出錯消息關(guān)鍵字(進入應(yīng)用程序的MessageResources 包)的字符串數(shù)組。controller servlet將作為適合于 <struts:errors> 標記使用的請求屬性保存這個數(shù)組,并且將控制重定向回輸入表單(由這個 ActionMapping 的 inputForm 屬性標識)。 
    正如以前提到的,這個特性完全是可選的。如果你的form bean 僅僅實現(xiàn)了 ActionForm 接口,controller servlet將假設(shè)任何請求的驗證由action類完成。 
    3.4 其它的表示技術(shù)
    盡管你的應(yīng)用程序的外表和感覺可以完全基于標準的JSP能力和Struts的定制標記庫構(gòu)建,你也應(yīng)該考慮展開其它改進組件重用、減少管理負擔或者減少出錯的技術(shù)。在下面的部分討論幾個可選的技術(shù)。 
    3.4.1 特定于應(yīng)用程序的定制標記
    在使用Struts庫提供的定制標記之外,很容易建立特定于你創(chuàng)建的應(yīng)用程序的標記來幫助建立用戶界面。Struts包括的例子程序用建立以下僅用于實現(xiàn)這個應(yīng)用程序的標記演示了這個原則: 
    checkLogon - 檢查一個特殊的會話對象的存在,如果不存在將控制重定向到注冊頁面。這是用來捕捉這樣的情況,用戶在你的應(yīng)用程序執(zhí)行的中間把一個頁面做成書簽并且試圖跳過注冊,或者用戶的會話超時。 
    linkSubscription - 為一個詳細的定單頁面生成一個超聯(lián)接,它將需要的主關(guān)鍵字值作為一個請求屬性傳遞。這在列出與一個用戶相關(guān)的定單并提供編輯或刪除定單的聯(lián)接時使用。 
    linkUser - 為一個用戶的一個具體的頁面生成一個超聯(lián)接,它將它將需要的主關(guān)鍵字值作為一個請求屬性傳遞。 
    這些標記的源代碼在 src/example 目錄中,在包 org.apache.struts.example 里,還帶有一些其它的用在這個應(yīng)用程序中的Java類。 
    3.4.2 有包含文件的頁面組件
    在一個JSP文件(包含定制標記和beans用來訪問請求的動態(tài)數(shù)據(jù))中創(chuàng)建完整的表示是一種非常普通的設(shè)計方法,在Struts包括的例子程序中被采用。然而很多應(yīng)用程序要求在單獨一個頁面中顯示你的應(yīng)用程序的多個邏輯上獨立的部分。 
    舉例來說,一個入口應(yīng)用程序可以在入口的主頁面上有一些或者全部以下的功能: 

    訪問這個入口的一個搜索引擎。 
    一個或更多的“提供新聞”的顯示,含有按照用戶的注冊信息定制的感興趣的標題。 
    訪問與這個入口相關(guān)的討論的主題。 
    如果你的入口提供免費郵件帳號,還要有一個“郵件等待”的提示。 
    如果你能夠?qū)⒐ぷ鲃澐珠_,分配不同的開發(fā)者去做不同的片段,那么這個站點不同片段的開發(fā)就會更加簡單。然后,你可以使用JSP技術(shù)的 include 能力來將這些片段組合進一個單獨的頁面。有兩種 include 可用,依賴于你希望輸出的組合發(fā)生在什么時間: 
    include 指令 (<%@ include file="xxxxx" %>)在JSP頁面被編譯時處理。它用于包括不需要在請求時改變的HTML代碼。它把包括進來的文本當作靜態(tài)文本,很象C或C++中的 #include 指令。 
    include 行為 (<jsp:include page="xxxxx" flush="true" />)在請求時處理,并且是由服務(wù)器透明處理。這意味著你可以通過把它嵌套在一個類似ifParameterEquals的標記中有條件地執(zhí)行include 。 
    3.4.3 圖片處理組件
    一些應(yīng)用程序要求動態(tài)生成圖片,就象一個股市報告站點的價格圖一樣。通常使用兩種不同的方法來實現(xiàn)這個需求: 
    處理一個執(zhí)行一個servlet請求的URL的超聯(lián)接。這個servlet將使用一個圖象庫來生成圖片,設(shè)置適當?shù)腸ontent類型(例如 image/gif),并且將圖片的字節(jié)流發(fā)送回瀏覽器。瀏覽器就會象從一個靜態(tài)文件中接收到的一樣顯示圖片。 
    處理HTML代碼需要下載一個創(chuàng)建請求的圖象的Java applet。你可以通過為在處理的代碼中的這個applet設(shè)置適當?shù)某跏蓟瘏?shù)配置這個圖象,或者你可以讓這個applet與服務(wù)器建立自己聯(lián)接來接收這些參數(shù)。 
    4. 創(chuàng)建Controller組件
    4.1 概述
    現(xiàn)在我們理解了怎樣構(gòu)造你的應(yīng)用程序的Model和View組件,現(xiàn)在是集中到 Controller 組件的時候了。Struts包括一個實現(xiàn)映射一個請求URI到一個行為類的主要功能的servlet。因此你的與Controller有關(guān)的主要責任是: 
    為每一個可能接收的邏輯請求寫一個 Action 類(也就是,一個 Action 接口的實現(xiàn)) 
    寫一個定義類名和與每個可能的映射相關(guān)的其它信息的 ActionMapping 類(也就是,一個 ActionMapping 接口的實現(xiàn)) 
    寫行為映射配置文件(用XML)用來配置controller servlet。 
    為你的應(yīng)用程序更新web應(yīng)用程序展開描述符文件(用XML)用來包括必需的Struts組件。 
    給你的應(yīng)用程序添加適當?shù)腟truts組件。 
    4.2 Action類
    Action 接口定義一個單一的必須由一個 Action 類實現(xiàn)的方法,就象下面這樣: 
    public ActionForward perform(ActionServlet servlet,
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
    throws IOException, ServletException;
    一個 Action 類的目標是處理這個請求,然后返回一個標識JSP頁面的 ActionForward 對象,控制應(yīng)該重定向這個JSP頁面以生成相應(yīng)的響應(yīng)。在 Model 2 設(shè)計模式中,一個典型的 Action 類將在它的 perform() 方法中實現(xiàn)下面的邏輯: 
    驗證用戶session的當前狀態(tài)(例如,檢查用戶已經(jīng)成功地注冊)。如果 Action 類發(fā)現(xiàn)沒有注冊存在,請求應(yīng)該重定向到顯示用戶名和口令用于注冊的JSP頁面。應(yīng)該這樣做是因為用戶可能試圖從“中間”(也就是,從一個書簽)進入你的應(yīng)用程序,或者因為session已經(jīng)超時并且servlet容器創(chuàng)建了一個新的session。 
    如果驗證還沒有發(fā)生(由于使用一個實現(xiàn) ValidatingActionForm 接口的form bean),驗證這個 form bean 的屬性是必須的。如果發(fā)現(xiàn)一個問題,當作一個請求屬性保存合適的出錯信息關(guān)鍵字,然后將控制重定向回輸入表單這樣錯誤可以被糾正。 
    執(zhí)行要求的處理來處理這個請求(例如在數(shù)據(jù)庫里保存一行)。這可以用嵌入 Action 類本身的代碼來完成,但是通常應(yīng)該調(diào)用一個商業(yè)邏輯bean的一個合適的方法來執(zhí)行。 
    更新將用來創(chuàng)建下一個用戶界面頁面的服務(wù)器端對象(典型情況下是request范圍或session范圍beans,定義你需要在多長時間內(nèi)保持這些項目可獲得)。 
    返回一個標識生成響應(yīng)的JSP頁面的適當?shù)?nbsp;ActionForward 對象,基于新近更新的beans。典型情況下,你將通過在你接收到的 ActionMapping 對象(如果你使用一個局部于與這個映射上的邏輯名)或者在controller servlet 本身(如果你使用一個全局于應(yīng)用程序的邏輯名)上調(diào)用 findForward() 得到一個對這樣一個對象的引用。 
    當為 Action 類編程時要記住的設(shè)計要點包括以下這些: 
    controller servlet僅僅創(chuàng)建一個你的 Action 類的實例,用于所有的請求。這樣你需要編寫你的 Action 類使其能夠在一個多線程環(huán)境中正確運行,就象你必須安全地編寫一個servlet的 service() 方法一樣。 
    幫助線程安全編程的最重要的原則就是在你的 Action 類中僅僅使用局部變量而不是實例變量。局部變量創(chuàng)建于一個分配給(由你的JVM)每個請求線程的棧中,所以沒有必要擔心會共享它們。 
    盡管不應(yīng)該,代表你的系統(tǒng)中Model部分的的beans仍有可能拋出違例。你應(yīng)該在你的 perform() 方法的邏輯中捕捉所有這樣的違例,并且通過執(zhí)行以下語句將它們記錄在應(yīng)用程序的日志文件中(包括相應(yīng)的棧跟蹤信息): 
    servlet.log("Error message text", exception);
    作為一個通用的規(guī)則,分配很少的資源并在來自同一個用戶(在用戶的session中)的請求間保持它們會導(dǎo)致可伸縮性的問題。你應(yīng)該在將控制重定向到適當?shù)腣iew組件前努力釋放這樣的資源(例如數(shù)據(jù)庫聯(lián)接) -- 甚至在你調(diào)用的一個bean拋出了一個違例時。 
    另外,你將會想要防止出現(xiàn)非常大的 Action 類。最簡單的實現(xiàn)途徑是將你的功能邏輯嵌入到 Action 類本身,而不是將其寫在獨立的商業(yè)邏輯beans中。除了使 Action 類難于理解和維護外,這種方法也使得難于重用這些商業(yè)邏輯代碼,因為代碼被嵌入到一個組件(Action 類)中并被捆綁運行于web應(yīng)用程序環(huán)境中。 
    包括在Struts中的例子程序某種程度上延伸了這個設(shè)計原則,因為商業(yè)邏輯本身是嵌入到 Action 類中的。這應(yīng)該被看作是在這個樣本應(yīng)用程序設(shè)計中的一個bug,而不是一個Struts體系結(jié)構(gòu)中的固有特性,或者是一個值得仿效的方法。 

    4.3 ActionMapping實現(xiàn)
    為了成功地運行,Struts的controller servlet需要知道關(guān)于每個URI該怎樣映射到一個適當?shù)?nbsp;Action 類的幾件事。需要了解的知識封裝在一個叫做 ActionMapping 的Java接口中,它有以下屬性: 
    actionClass - 用于這個映射的 Action 類完整的Java類名。第一次一個特定的映射被使用,一個這個類的實例將被創(chuàng)建并為以后重用而保存。 
    formAttribute - session范圍的bean的名字,當前的這個映射的 ActionForm 被保存在這個bean之下。如果這個屬性沒有被定義,沒有 ActionForm 被使用。 
    formClass - 用于這個映射的 ActionForm 類完整的Java類名。如果你在使用對form beans的支持,這個類的一個實例將被創(chuàng)建并保存(在當前的用戶會話中) 
    path - 匹配選擇這個映射的請求的URI路徑。看下面如何匹配的例子。 
    Struts在一個叫做 ActionMappingBase 的類中包括了一個 ActionMapping 接口的方便的實現(xiàn)。如果你不需要為你自己的映射定義任何附加的屬性,盡管把這個類作為你的 ActionMapping 類好了,就向下面部分描述的那樣配置。然而,定義一個 ActionMapping 實現(xiàn)(多半是擴展 ActionMappingBase 類)來包含附加的屬性也是可能的。controller servlet知道怎樣自動配置這些定制屬性,因為它使用Struts的Digester模塊來讀配置文件。 
    包括在Struts的例子程序中,這個特性用來定義兩個附加的屬性: 

    failure - 如果Action類檢測到它接收的輸入字段的一些問題,控制應(yīng)該被重定向到的上下文相關(guān)的URI。典型情況下是請求發(fā)向的JSP頁面名,它將引起表單被重新顯示(包含Action類設(shè)置的出錯消息和大部分最近的來自ActionForm bean的輸入值)。 
    success - 如果Action類成功執(zhí)行請求的功能,控制應(yīng)該被重定向到的上下文相關(guān)的URI。典型情況下是準備這個應(yīng)用程序的會話流的下一個頁面的JSP頁面名。 
    使用這兩個額外的屬性,例子程序中的 Action 類幾乎完全獨立于頁面設(shè)計者使用的實際的JSP頁面名。這個頁面可以在重新設(shè)計時被重命名,然而幾乎不會影響到 Action 類本身。如果“下一個”JSP頁面的名字被硬編碼到 Action 類中,所有的這些類也需要被修改。 
    4.4 Action映射配置文件
    controller servlet怎樣知道你想要得到的映射?寫一個簡單地初始化新的 ActionMapping 實例并且調(diào)用所有適當?shù)膕et方法的小的Java類是可能的(但是很麻煩)。為了使這個處理簡單些,Struts包括一個Digester模塊能夠處理一個想得到的映射的基于XML的描述,同時創(chuàng)建適當?shù)膶ο蟆??nbsp;API 文檔 以獲得關(guān)于Digester更多的信息。 
    開發(fā)者的責任是創(chuàng)建一個叫做 action.xml 的XML文件,并且把它放在你的應(yīng)用程序的WEB-INF目錄中。(注意這個文件并不需要 DTD,因為實際使用的屬性對于不同的用戶可以是不同的)最外面的XML元素必須是<action-mappings>,在這個元素之中是嵌入的0個或更多的 <action> 元素 -- 每一個對應(yīng)于你希望定義的一個映射。 

    來自例子程序的 action.xml 文件包括“注冊”功能的以下映射條目,我們用來說明這個需求: 

    <action-mappings>

    <forward name="logon" path="/logon.jsp"/>

    <action path="/logon"
    actionClass="org.apache.struts.example.LogonAction"
    formAttribute="logonForm"
    formClass="org.apache.struts.example.LogonForm"
    inputForm="/logon.jsp">
    <forward name="success" path="/mainMenu.jsp"/>
    </action>

    </action-mappings>
    就象你所看到的,這個映射匹配路徑 /logon (實際上,因為例子程序使用擴展匹配,你在一個JSP頁面指定的請求的URI結(jié)束于/logon.do)。當接收到一個匹配這個路徑的請求時,一個 LogonAction 類的實例將被創(chuàng)建(僅僅在第一次)并被使用。controller servlet將在關(guān)鍵字 logonForm 下查找一個session范圍的bean,如果需要就為指定的類創(chuàng)建并保存一個bean。 
    這個 action 元素也定義了一個邏輯名“success”,它在 LogonAction 類中被用來標識當一個用戶成功注冊時使用的頁面。象這樣使用一個邏輯名允許將 action 類隔離于任何由于重新設(shè)計位置而可能發(fā)生的頁面名改變。 

    這是第二個在任何 action 之外宣告的 forward 元素,這樣它就可以被所有的action全局地獲得。在這個情況下,它為注冊頁面定義了一個邏輯名。當你調(diào)用 mapping.findForward() 時在你的 action 代碼中,Struts首先查找這個action本地定義的邏輯名。如果沒有找到,Struts會自動為你查找全局定義的邏輯名。 

    4.5 Web應(yīng)用程序展開描述符
    設(shè)置應(yīng)用程序最后的步驟是配置應(yīng)用程序展開描述符(保存在文件WEB-INF/web.xml中)以包括所有必需的Struts組件。作為一個指南使用例子程序的展開描述符,我們看到下面的條目需要被創(chuàng)建或修改。 
    4.5.1 配置Action Servlet實例
    添加一個條目定義action servlet本身,同時包括適當?shù)某跏蓟瘏?shù)。這樣一個條目看起來象是這樣: 
    <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-p
    aram>
    <param-name>application</param-name>
    <param-value>org.apache.struts.example.ApplicationResources</param-value>
    </init-param>
    <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/action.xml</param-value>
    </init-param>
    <init-param>
    <param-name>debug</param-name>
    <param-value>2</param-value>
    </init-param>
    <init-param>
    <param-name>mapping</param-name>
    <param-value>org.apache.struts.example.ApplicationMapping</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
    </servlet>
    controller servlet支持的初始化參數(shù)在下面描述,拷貝自 ActionServlet 類的 Javadocs 。方括號描述如果你沒有為那個初始化參數(shù)提供一個值時假設(shè)的缺省值。 
    application - 應(yīng)用程序資源包基類的Java類名。[NONE]. 
    config - 包含配置信息的XML資源的上下文相關(guān)的路徑。[/WEB-INF/action.xml] 
    debug - 這個servlet的調(diào)試級別,它控制記錄多少信息到日志中。[0] 
    digester - 我們在 initMapping() 中利用的Digester的調(diào)試級別,它記錄到System.out而不是servlet的日志中。[0] 
    forward - 使用的ActionForward實現(xiàn)的Java類名。[org.apache.struts.action.ActionForward] 
    mapping - 使用的ActionMapping實現(xiàn)的Java類名。[org.apache.struts.action.ActionMappingBase] 
    nocache - 如果設(shè)置為 true,增加HTTP頭信息到所有響應(yīng)中使瀏覽器對于生成或重定向到的任何響應(yīng)不做緩沖。[false] 
    null - 如果設(shè)置為 true,設(shè)置應(yīng)用程序資源使得如果未知的消息關(guān)鍵字被使用則返回 null。否則,一個包括不歡迎的消息關(guān)鍵字的出錯消息將被返回。[true] 
    4.5.2 配置Action Servlet映射
    有兩種通常的方法來定義將被controller servlet處理的URL -- 前綴匹配和擴展匹配。每種方法的一個適當?shù)挠成錀l目將在下面被描述。 
    前綴匹配意思是你想讓所有以一個特殊值開頭(在上下文路徑部分之后)的URL傳遞給這個servlet。這樣一個條目看起來可以象是這樣: 

    <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>/execute/*</url-pattern>
    </servlet-mapping>
    它意味著一個匹配前面描述的 /logon 路徑的請求的URL看起來象是這樣: 
    http://www.mycompany.com/myapplication/execute/logon
    這里 /myapplicationis 是你的應(yīng)用程序展開所在的上下文路徑。 
    另一方面,擴展映射基于URL以一個跟著定義的一組字符的句點結(jié)束的事實而將URL匹配到action servlet 。例如,JSP處理servlet映射到 *.jsp 模式這樣它在每個JSP頁面請求時被調(diào)用。為了使用 *.do 擴展(它意味著“做某件事”)映射條目看起來應(yīng)該象是這樣: 

    <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    并且一個匹配以前描述的 /logon 路徑的請求的URI可以看起來象是這樣: 
    http://www.mycompany.com/myapplication/logon.do
    4.5.3 配置Struts標記庫
    下一步,你必須添加一個定義Struts標記庫的條目。這個條目看起來應(yīng)該象是這樣: 
    <taglib>
    <taglib-uri>/WEB-INF/struts.tld</taglib-uri>
    <taglib-location>/WEB-INF/struts.tld</taglib-location>
    </taglib>
    它告訴JSP系統(tǒng)到哪里去找這個庫的標記庫描述符(在你的應(yīng)用程序的WEB-INF目錄,而不是在外部互聯(lián)網(wǎng)上的某個地方)。 
    4.5.4 添加Struts組件到你的應(yīng)用程序中
    為了在你的應(yīng)用程序運行時使用Struts,你必須將 struts.tld 文件拷貝到你的 WEB-INF 目錄,將 struts.jar 文件拷貝到你的 WEB-INF/lib 。 




    posted on 2005-03-02 22:48 小魚兒 閱讀(569) 評論(0)  編輯  收藏 所屬分類: MVC
    主站蜘蛛池模板: yy6080亚洲一级理论| 久久久久久亚洲精品无码| 国产成人精品日本亚洲专区 | 久久大香伊焦在人线免费| 国产亚洲美女精品久久| 亚洲1234区乱码| 亚洲天天做日日做天天欢毛片| 四虎精品亚洲一区二区三区| 成年在线网站免费观看无广告| 久久成人免费播放网站| 在线视频网址免费播放| 黄网站在线播放视频免费观看| 亚洲国产日韩视频观看| 久久久亚洲欧洲日产国码是AV| 国产亚洲成AV人片在线观黄桃| 亚洲av再在线观看| 国产中文字幕免费观看| 美女黄网站人色视频免费国产| 91精品国产免费久久久久久青草| 国产免费AV片在线观看| 成在线人免费无码高潮喷水| 无码人妻一区二区三区免费视频| 亚洲欧美国产国产综合一区| 亚洲一区精彩视频| 久久综合久久综合亚洲| 亚洲一级毛片免费在线观看| 亚洲国产人成在线观看| 亚洲美女中文字幕| 亚洲经典在线中文字幕| 久久亚洲sm情趣捆绑调教| 亚洲色图在线观看| 亚洲精品视频在线观看视频| 亚洲视频在线免费播放| 亚洲资源在线视频| 精品久久亚洲中文无码| 亚洲卡一卡二卡乱码新区| 狠狠色伊人亚洲综合网站色 | 免费无码又爽又刺激聊天APP| 免费在线看v网址| 最近免费中文字幕4| 成人毛片免费在线观看|