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

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

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

    隨筆-57  評(píng)論-117  文章-1  trackbacks-0

    Struts 概述

    隨著MVC 模式的廣泛使用,催生了MVC 框架的產(chǎn)生。在所有的MVC 框架中,出現(xiàn)最早,應(yīng)用最廣的就是Struts 框架。

    Struts 的起源

    Struts 是Apache 軟件基金組織Jakarta 項(xiàng)目的一個(gè)子項(xiàng)目, Struts 的前身是CraigR. McClanahan 編寫(xiě)的JSP Model2 架構(gòu)。

    Struts 在英文中是"支架、支撐"的意思,這表明了Struts 在Web 應(yīng)用開(kāi)發(fā)中的巨大作用,采用Struts 可以更好地遵循MVC 模式。此外, Struts 提供了一套完備的規(guī)范,以基礎(chǔ)類(lèi)庫(kù),可以充分利用JSP/Servlet 的優(yōu)點(diǎn),減輕程序員的工作量,具有很強(qiáng)的可擴(kuò)展性。

    Struts優(yōu)點(diǎn)

    提高開(kāi)發(fā)效率,減輕了程序員的工作量,降低了重復(fù)代碼(降低代碼冗余),文件不再臃腫。

    可以規(guī)范軟件開(kāi)發(fā)的行為。ActionForm為我們封裝請(qǐng)求數(shù)據(jù)

    增加代碼的擴(kuò)展性、移植性

    提高代碼的可重用性、可讀性,無(wú)需多個(gè)Servlet多個(gè)方法

    Action轉(zhuǎn)發(fā)頁(yè)面只須配置跳轉(zhuǎn)資源即可,無(wú)效全路徑、硬編碼。降低代碼的耦合性

    Struts 架構(gòu)的工作原理

    1. Model 部分

    Struts 的Model 部分由ActionForm和JavaBean 組成。其中ActionForm用于封裝用戶(hù)請(qǐng)求參數(shù),所有的用戶(hù)請(qǐng)求參數(shù)由系統(tǒng)自動(dòng)封裝成ActionForm 對(duì)象:該對(duì)象被ActionServlet轉(zhuǎn)發(fā)給Action; 然后Action 根據(jù)ActionForm里的請(qǐng)求參數(shù)處理用戶(hù)請(qǐng)求。JavaBean 則封裝了底層的業(yè)務(wù)邏輯,包括數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)等。在更復(fù)雜的應(yīng)用中,JavaBean所代表的絕非一個(gè)簡(jiǎn)單的JavaBean,可能是EJB 組件或者其他的業(yè)務(wù)邏輯組件。該Model 對(duì)應(yīng)圖3 .4的Model 部分。

    2. View 部分

    Struts 的View 部分采用JSP 實(shí)現(xiàn)。Struts 提供了豐富的標(biāo)簽庫(kù),通過(guò)這些標(biāo)簽庫(kù)可以最大限度地減少腳本的使用。這些自定義的標(biāo)簽庫(kù)可以實(shí)現(xiàn)與Model 的有效交互,并增加了顯示功能。對(duì)應(yīng)圖的JSP 部分。

    整個(gè)應(yīng)用由客戶(hù)端請(qǐng)求驅(qū)動(dòng),當(dāng)客戶(hù)端請(qǐng)求被ActionServlet 攔截時(shí), ActionServlet根據(jù)請(qǐng)求決定是否需要調(diào)用Model 處理用戶(hù)請(qǐng)求,當(dāng)用戶(hù)請(qǐng)求處理完成后,其處理結(jié)果通過(guò)JSP 呈現(xiàn)給用戶(hù)。

    3. Controller部分

    Struts 的Controller 由兩個(gè)部分組成。

    ·系統(tǒng)核心控制器—攔截用戶(hù)請(qǐng)求ActionServlet 派發(fā)請(qǐng)求

    ·業(yè)務(wù)邏輯控制器—處理用戶(hù)請(qǐng)求的Action,處理業(yè)務(wù)邏輯

    其中,系統(tǒng)核心控制器是一個(gè)ActionServlet。該控制器由Struts 框架提供,繼承HttpServlet類(lèi),因此可以配置成一個(gè)標(biāo)準(zhǔn)的Servlet。該控制器負(fù)責(zé)攔截所有Http請(qǐng)求,然后根據(jù)用戶(hù)請(qǐng)求決定是否需要調(diào)用業(yè)務(wù)邏輯控制器,如果需要調(diào)用業(yè)務(wù)邏輯控制器,則將請(qǐng)求轉(zhuǎn)發(fā)給Action 處理,否則直接轉(zhuǎn)向請(qǐng)求的JSP 頁(yè)面。業(yè)務(wù)邏輯控制器負(fù)責(zé)處理用戶(hù)請(qǐng)求,但業(yè)務(wù)邏輯控制器本身并不具有處理能力,而是調(diào)用Model 來(lái)完成處理。業(yè)務(wù)邏輯控制器對(duì)應(yīng)圖3 .4中的Action 部分。

    clip_image002

    下面結(jié)合圖3.7 對(duì)Struts 的工作流程作詳細(xì)的講解。

    Web 應(yīng)用都是請(qǐng)求一響應(yīng)的程序結(jié)構(gòu)。程序是由客戶(hù)端Client 發(fā)出Http 請(qǐng)求開(kāi)始的,客戶(hù)端請(qǐng)求被ActionServlet 攔截。在ActionServlet 處,有兩種情況:

    ·要求邏輯控制器處理的請(qǐng)求:

    ·簡(jiǎn)單轉(zhuǎn)發(fā)的請(qǐng)求。

    對(duì)于第一種的請(qǐng)求,ActionServlet 需要調(diào)用對(duì)應(yīng)的Action。因此ActionServlet 將請(qǐng)求轉(zhuǎn)發(fā)到Action ,如果請(qǐng)求還配置了對(duì)應(yīng)的FormBean,則ActionServlet 還負(fù)責(zé)用請(qǐng)求參數(shù)填充ActionForm,此時(shí)如果ActionForm還沒(méi)有創(chuàng)建。ActionServlet會(huì)幫我們創(chuàng)建一個(gè)可以用的ActionForm,如果ActionForm已經(jīng)創(chuàng)建就直接給我們用, ActionForm 的實(shí)質(zhì)就是JavaBean,專(zhuān)門(mén)用于封裝請(qǐng)求參數(shù)。并且在次期間,如果ActionForm如果有驗(yàn)證方法,會(huì)去執(zhí)行驗(yàn)證方法,如果驗(yàn)證通過(guò)會(huì)進(jìn)入Action中。驗(yàn)證失敗,會(huì)跳轉(zhuǎn)到Action配置的input資源頁(yè)面。

    此時(shí)的Action 將無(wú)須從HTTP Request 中獲取請(qǐng)求參數(shù),而是從ActionForm 中獲得請(qǐng)求參數(shù)。Action 獲得請(qǐng)求參數(shù)后,調(diào)用Model 對(duì)象由JavaBean 處理用戶(hù)請(qǐng)求。Action處理完用戶(hù)請(qǐng)求之后,將處理結(jié)果包裝成ActionForward,回送給ActionServlet。

    由于ActionForward 對(duì)象封裝了JSP 資源的映射。因此, ActionServlet 知道調(diào)用合適的JSP 資源表現(xiàn)給客戶(hù)端。

    對(duì)于第二種請(qǐng)求, HTTP 請(qǐng)求無(wú)須Action 處理,只是對(duì)普通資源的請(qǐng)求,作為超級(jí)鏈接的替代。因?yàn)锳ctionServlet 直接將該請(qǐng)求轉(zhuǎn)發(fā)給JSP 資源,既不會(huì)填充ActionForm,也無(wú)須調(diào)用Action 處理。

    JSP 頁(yè)面在表現(xiàn)之前,還需要調(diào)用對(duì)應(yīng)的JavaBean,此處的JavaBean 不再是包含業(yè)務(wù)邏輯的JavaBean,而是封裝了處理結(jié)果的普通vo (值對(duì)象)。JSP 頁(yè)面根據(jù)vo 的值,可能利用JSTL 或者Struts 的標(biāo)簽庫(kù)來(lái)生成HTTP 響應(yīng)給客戶(hù)端。總之JSP 應(yīng)盡量避免使用Java 腳本。

    Action配置

    path是我們請(qǐng)求訪(fǎng)問(wèn)的路徑,如果用struts標(biāo)簽,會(huì)默認(rèn)加上.do的后綴。ActionServlet攔截到*.do的請(qǐng)求后,就進(jìn)行相應(yīng)的業(yè)務(wù)處理,然后派發(fā)到path對(duì)應(yīng)的Action;

    name是Action對(duì)象的ActionForm,ActionForm是封裝請(qǐng)求的信息,如表單

    attribute和name一樣,可以省略,在省略的情況下用name。都是對(duì)應(yīng)ActionForm

    type是Action對(duì)象對(duì)應(yīng)的文件路徑,含包名

    scope是ActionForm的作用域,默認(rèn)request

    parameter后帶方法名稱(chēng),即請(qǐng)求所執(zhí)行的方法

    forward是轉(zhuǎn)發(fā)后的資源頁(yè)面

    ActionForward配置

    name邏輯名稱(chēng)和Action中的mapping.forward參數(shù)對(duì)應(yīng)

    path對(duì)應(yīng)映射的JSP頁(yè)面

    redirect是否重定向請(qǐng)求

    forward有全局和局部的2種,如果當(dāng)前Action配置的forward資源在當(dāng)前配置的Action中沒(méi)有找到,然后回到全局的forward資源中查找。局部?jī)?yōu)先全局

    ActonForm配置

    name是form的名稱(chēng)

    type是form的包名+文件名

    ActionForm還有動(dòng)態(tài)ActionForm、驗(yàn)證ActionForm

    國(guó)際化I18N(Internationalization

    目的:是適應(yīng)更多的、更好的用戶(hù)界面

    Java 程序的國(guó)際化主要通過(guò)如下三個(gè)類(lèi)完成。

       java.util. ResourceBundle: 對(duì)應(yīng)用于加載一個(gè)資源包。

       java.util.Locale: 對(duì)應(yīng)一個(gè)特定的國(guó)家/區(qū)域及語(yǔ)言環(huán)境。

       java.text.MessageFormat: 用于將消息格式化。

    為了實(shí)現(xiàn)程序的國(guó)際化,必須先提供程序所需要的資源文件。資源文件的內(nèi)容是和很多key-value 對(duì)。其中key 是程序使用的部分,而value 則是程序界面的顯示。

    資源文件的命名可以有如下三種形式。

       baseName _language_country.properties。

       baseName _language.properties。

       baseNarne.properties 。

    其中baseName 是資源文件的基本名,用戶(hù)可以自由定義。而language 和count可都不可隨意變化,必須是Java 所支持的語(yǔ)言和國(guó)家。

    1.國(guó)際化支持的語(yǔ)言和國(guó)家

    事實(shí)上, Java 也不可能支持所有國(guó)家和語(yǔ)言,如需要獲取Java 所支持的語(yǔ)言和國(guó)家,可調(diào)用Locale 類(lèi)的getAvailableLocale 方法來(lái)獲取。該方法返回一個(gè)Locale 數(shù)組,該數(shù)組里包含了Java 所支持的語(yǔ)言和國(guó)家。

    2. 編寫(xiě)國(guó)際化所需的資源

    國(guó)際化所需的資源文件內(nèi)容是key-value 對(duì),下面提供了兩個(gè)資源文件,這兩個(gè)資源文件很簡(jiǎn)單,只包含一個(gè)key-value 對(duì)。

    下面是MyResource.properties 的文件的內(nèi)容:

    資源文件的內(nèi)容: key-value 對(duì)。

    msg=Hello , {O} Today is {1}.

    下面是MyResource_zh_CN.properties 文件的內(nèi)容:

    資源文件的內(nèi)容: key-value 對(duì)

    msg=你好. {O} 今天是{l}。

    所有資源文件的key 都是相同的,只是value 會(huì)隨國(guó)家和語(yǔ)言的不同而變化。

    3.程序從哪里獲取資源呢?

    在ResourceBundle 加載資源時(shí)按如下順序搜索。

    搜索所有國(guó)家和語(yǔ)言都匹配的資源文件,例如,對(duì)于簡(jiǎn)體中文的環(huán)境,先搜索如下文件:

    MyResource_zh_CN.properties

    如果沒(méi)有找到國(guó)家和語(yǔ)言都匹配的資源文件,則再搜索語(yǔ)言匹配的文件,即搜索如下文件:

    MyResource_zh.properties

    如果上面的文件依然無(wú)法搜索到,則搜索baseNarne 匹配的文件,即搜索如下文件:

    MyResource.properties

    4. 使用類(lèi)文件代替資源文件

    Java 也允許使用類(lèi)文件代替資源文件,即將所有的key-value對(duì)存入class 文件,而不是屬性文件。

    用來(lái)代替資源文件的Java 文件必須滿(mǎn)足如下條件。

    ·類(lèi)的名字必須為baseNarne_language_country,這與屬性文件的命名相似。

    ·該類(lèi)必須繼承ListResourceBundle,并重寫(xiě)getContents 方法,該方法返回Object數(shù)組,該數(shù)組的每一個(gè)項(xiàng)都是key=value 對(duì)。

    eg:下面的類(lèi)文件可以代替上面的屬性文件:

    public class MyResource_zh_CN extends ListResourceBundle {

        // 定義資源

        private final Object myData[][] = { "msg" , " {0}您好!今天是{l} "};

        //重寫(xiě)方法getContents()

        public Object[] [] getContents() {

            //該方法返回資源的key-value對(duì)

            return myData;

        }

    }

    如果系統(tǒng)同時(shí)存在資源文件及類(lèi)文件,則系統(tǒng)將以類(lèi)文件為主,而不會(huì)調(diào)用資源文件。對(duì)于簡(jiǎn)體中文的Locale, ResourceBundle 搜索資源的順序是:

    (1) baseName zh CN.class 。

    (2) baseNarne_zh_CN.properties。

    (3) baseNarne zh.class 。

    (4) baseNarne_zh.properties。

    (5) baseNarne.class。

    (6) baseNarne.properties。

    當(dāng)系統(tǒng)按上面的順序搜索資源文件時(shí),如果前面的文件不存在,則會(huì)使用下一個(gè):如果一直找不到對(duì)應(yīng)的文件,系統(tǒng)將拋出異常。

    struts加載資源文件

    資源文件的加載通過(guò)struts-config.xml文件來(lái)配置,加載資源文件應(yīng)從Web 應(yīng)用的WEB-INF/classes路徑開(kāi)始加載。因此,資源文件必須放在WEB-INF/classes路徑或該路徑的子路徑下。如果直接放在WEB-INF/classes 路徑下,在配置資源文件時(shí),直接指定資源文件的baseName 即可。但如果放在子路徑下,則必須以包的形式配置。

    動(dòng)態(tài)ActionForm

    Why當(dāng)一個(gè)form表單的屬性、字段非常多的情況下,需要我們不斷的修改、添加ActionForm中的屬性,并提供getter、setter方法。雖然這個(gè)類(lèi)比較簡(jiǎn)單,但是大量重復(fù)的getter、setter方法也是比較繁瑣的。這個(gè)時(shí)候struts的動(dòng)態(tài)ActionForm就派上用場(chǎng)了。使用動(dòng)態(tài)ActionForm 的目的是為了減少代碼的書(shū)寫(xiě)量,但是相對(duì)在配置方面要復(fù)雜些。

    配置動(dòng)態(tài)ActionForm

    所有的動(dòng)態(tài)ActionForm 的實(shí)現(xiàn)類(lèi)都必須是org.apache.struts.action.DynaActionForm類(lèi),或者是它的子類(lèi)。使用動(dòng)態(tài)ActionForm 與前面不同的是:因?yàn)橄到y(tǒng)不清楚動(dòng)態(tài)ActionForm 的屬性,所以必須在配置文件中配置對(duì)應(yīng)的屬性。可以使用form-property 元素來(lái)配置動(dòng)態(tài)ActionForm 的屬性。

    <!一配置動(dòng)態(tài)ActionForm,動(dòng)態(tài)Aciton 必須使用烏rnaActionForm 一〉

    <form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm">

         <!一配置ActionForm的屬性: username-->

         <form-property name="username" type="java.lang.String"/>

         <! 配置ActionForm的屬性: pass-->

         <form-property name="pass"type="java.lang.String"/>

    </form-bean>

     

    <!-- 配置Action中的path , type , name 屬性>

    <action path="/login" type="com.hoo.LoginAction" name="loginForm">

         <!一配置兩個(gè)局部Forward-->

         <forward name="welcome" path="/WEB-INF/jsp/welcome.jsp"/>

         <forward name="input" path="/login.jsp"/>

    </action>

    從上面的配置文件可看出,動(dòng)態(tài)ActionForm 的配置必須增加form-property 元素,每個(gè)屬性必須對(duì)應(yīng)一個(gè)form-property元素。

    form-property元素包含兩個(gè)屬性。

    name: 屬性的名字,必須與JSP 頁(yè)面的表單域的名字相同。

    type: 屬性的類(lèi)型。

     

    使用動(dòng)態(tài)ActionForm

    //必須重寫(xiě)該核心方法,該方法actionForm 將表單的請(qǐng)求參數(shù)封裝成值對(duì)象

    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

         //將ActionForm強(qiáng)制類(lèi)型轉(zhuǎn)換為DynaActionForm

         DynaActionForm loginForm = (DynaActionForm)form;

         //從ActionForm中解析出請(qǐng)求參數(shù): username

         String username = (String)loginForm.get("username");

         //從ActionForm中解析出請(qǐng)求參數(shù): password

         String pass = (String)loginForm.get("pass");

         //后面的處理與前一個(gè)示例的Action 相同。

         …………

    }

    使用動(dòng)態(tài)ActionForm 時(shí),請(qǐng)求參數(shù)必須使用DynaActionForm的getter 方法獲取。

    DynaActionForm 的getter 方法主要有如下三個(gè)。

    Object get(java.lang.String name): 根據(jù)屬性名返回對(duì)應(yīng)的值。

    Object get(java.lang.String name, int index): 對(duì)于有多個(gè)重名表單域的情況, Struts將其當(dāng)成數(shù)組處理,此處根據(jù)表面域名和索引獲取對(duì)應(yīng)值。

    Object get(java.lang.String name, java.lang.String key): 對(duì)于使用Map 屬性的情況,根據(jù)屬性名及對(duì)應(yīng)key. 獲取對(duì)應(yīng)的值。

     

    Struts 的標(biāo)簽庫(kù)

    Struts 提供了大量的標(biāo)簽庫(kù),用于完成表現(xiàn)層的輸出。借助于Struts 的標(biāo)簽庫(kù),可避免在JSP 中嵌入大量的Java 腳本,從而提高代碼的可讀性。

    Struts 主要提供了如下三個(gè)標(biāo)簽庫(kù)。

    A、 html: 用于生成HTML 的基本標(biāo)簽。

    B、 bean: 用于完成程序國(guó)際化,輸出Struts 的ActionForm 的屬性值等。

    C、 logic: 用于完成循環(huán)、選擇等流程控制。

     

    使用html 標(biāo)簽庫(kù)

    Struts 為htrnl 的大部分標(biāo)簽提供了對(duì)應(yīng)的htrnl 標(biāo)簽, htrnl 所支持的標(biāo)簽大致有如下。

    * base: 表現(xiàn)成一個(gè)HTML 的<base>標(biāo)簽。

    * button: 表現(xiàn)成一個(gè)按鈕,該按鈕默認(rèn)沒(méi)有任何動(dòng)作。

    * cancel: 表現(xiàn)成一個(gè)取消按鈕。

    * checkbox: 表現(xiàn)成一個(gè)Checkbox 的輸入框。

    * error: 用于輸出數(shù)據(jù)校驗(yàn)的出錯(cuò)提示。

    * file: 表現(xiàn)成一個(gè)文件瀏覽輸入框。

    * form: 表現(xiàn)成一個(gè)form 域。

    * frame: 表現(xiàn)成一個(gè)HTML<frame>標(biāo)簽。

    * hidde: 表現(xiàn)成一個(gè)隱藏域。

    * htrnl: 表現(xiàn)成HTML 的<htrnl>標(biāo)簽。

    * image: 表現(xiàn)成表單域的image 域。

    * img: 表現(xiàn)成一個(gè)HTML 的img 標(biāo)簽。

    * javascrit: 表現(xiàn)成JavaScript 的校驗(yàn)代碼,這些校驗(yàn)代碼根據(jù)ValidatorPlugIn 生成。

    * link: 表現(xiàn)成HTML 的超級(jí)鏈接。

    * messages: 用于輸出Struts 的各種提示信息,包括校驗(yàn)提示。

    * multibox: 表現(xiàn)成一個(gè)Checkbox 輸入框。

    * option: 表現(xiàn)成選擇框的一個(gè)選項(xiàng)。

    * password: 表現(xiàn)成一個(gè)密碼輸入框。

    * radio: 表現(xiàn)成一個(gè)單選輸入框。

    * reset: 表現(xiàn)成一個(gè)重設(shè)按鈕。

    * rewrite: 表現(xiàn)成一個(gè)URL 。

    * select: 表現(xiàn)成一個(gè)列表選擇框。

    * submit: 表現(xiàn)成一個(gè)提交按鈕。

    * text: 表現(xiàn)成一個(gè)單行文本輸入框。

    * textarea: 表現(xiàn)成一個(gè)多行文本框。

     

    使用bean 標(biāo)簽庫(kù)

    bean 標(biāo)簽庫(kù)主要用于輸出屬性值、提示消息及定義請(qǐng)求參數(shù)等。下面是bean 標(biāo)簽庫(kù)的常用標(biāo)簽。

    * cookie: 將請(qǐng)求的cookie 的值定義成腳本可以訪(fǎng)問(wèn)的JavaBean 實(shí)例。

    * define: 將某個(gè)bean 的屬性值定義成腳本可以訪(fǎng)問(wèn)的變量。

    * header: 將請(qǐng)求頭的值定義成腳本可以訪(fǎng)問(wèn)的變量。

    * include: 將某個(gè)JSP 資源完整定義成一個(gè)bean 實(shí)例。

    * message: 用于輸出國(guó)際化信息。

    * page: 將page Context 中的特定項(xiàng)定義成一個(gè)bean 。

    * parameter: 將請(qǐng)求參數(shù)定義成腳本可以訪(fǎng)問(wèn)的變量。

    * resource: 加載Web 應(yīng)用的資源,并將其變成JavaB eano

    * struts: 用于將某個(gè)Struts 的內(nèi)部配置成一個(gè)bean 。

    * write: 用于輸出某個(gè)bean 的屬性值。

     

    使用logic 標(biāo)簽庫(kù)

    logic 標(biāo)簽庫(kù)是使用最頻繁,相對(duì)復(fù)雜的標(biāo)簽庫(kù)。logic 標(biāo)簽庫(kù)主要用于完成基本的流程控制,比如循環(huán)及選擇等。

    logic 標(biāo)簽庫(kù)主要有如下標(biāo)簽。

    * empty: 如果給定的變量為空或者為空字符串,則就計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * equal: 如果給定變量與特定的值相等,則會(huì)計(jì)算并輸出該標(biāo)簽體的內(nèi)容。

    * forward: 將某個(gè)頁(yè)面的控制權(quán)forward 確定的ActionForward 項(xiàng)。

    * greaterEqual: 如果給定變量大于或等于特定的值,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * greaterThan: 如果給定變量大于特定的值,則會(huì)計(jì)算井輸出標(biāo)簽體的內(nèi)容。

    * iterate: 通過(guò)遍歷給定集合的元素,對(duì)標(biāo)簽體的內(nèi)容進(jìn)行循環(huán)。

    * lessEqual: 如果給定變量小于或等于特定的值,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * lessThan: 如果給定變量小于特定的值,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * match: 如果特定字符串是給定消息合適的子字符串,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * messagesNotPresent: 如果請(qǐng)求中不包含特定的消息內(nèi)容,將計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * messagesPresent: 如果請(qǐng)求中包含特定的消息內(nèi)容,則計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * notEmpty: 如果給定的變量既不為空,也不是空字符串,則計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * notEqual: 如果給定變量不等于特定的值,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * notMatch: 如果特定宇符串不是給定消息合適的子字符串,則會(huì)計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * notPresent: 如果特定的值沒(méi)有出現(xiàn)在請(qǐng)求中,則計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * present: 如果特定的值出現(xiàn)在請(qǐng)求中,則計(jì)算并輸出標(biāo)簽體的內(nèi)容。

    * redirect: 重定向頁(yè)面。

     

    Struts 的數(shù)據(jù)校驗(yàn)

    數(shù)據(jù)校驗(yàn)也稱(chēng)為輸入校驗(yàn),指導(dǎo)對(duì)用戶(hù)的輸入進(jìn)行基本過(guò)濾,包括必填宇段,宇段必須為數(shù)字及兩次輸入的密碼必須相匹配等。這些是每個(gè)MVC 框架都應(yīng)該完成的任務(wù),Struts 提供了基本的數(shù)據(jù)校驗(yàn),如果結(jié)合commons-validator, Struts 則擁有強(qiáng)大的校驗(yàn)框架,包括進(jìn)行客戶(hù)端的JavaScript 校驗(yàn)等。

    Struts 的數(shù)據(jù)校驗(yàn)大致有如下幾種方式:

    ActionForm 的代碼校驗(yàn)。

    Action 里的代碼校驗(yàn)。

    結(jié)合commons-validator.jar 的校驗(yàn)。

     

    ActionForm 的代碼校驗(yàn)

    ActionForm 的代碼校驗(yàn)是最基本的校驗(yàn)方式。這種校驗(yàn)方式是重寫(xiě)ActionForm 的validate 方法,在該方法內(nèi)對(duì)所有的宇段進(jìn)行基本校驗(yàn)。如果出現(xiàn)不符合要求的輸出,則將出錯(cuò)提示封裝在ActionError 對(duì)象里,最后將多個(gè)ActionError 組合成ActionErrors 對(duì)象,該對(duì)象里封裝了全部的出錯(cuò)提示。并將錯(cuò)誤信息用<html:error/>展現(xiàn)在配置的input的失敗頁(yè)面上。

     

    Action 的代碼校驗(yàn)

    在Action 里通過(guò)代碼完成輸入校驗(yàn),是最基本,也最容易使用的方法。與最初的MVC 設(shè)計(jì)相似,在調(diào)用業(yè)務(wù)邏輯組件之前,先對(duì)數(shù)據(jù)進(jìn)行基本校驗(yàn)。這是最傳統(tǒng)也是最原始的方法。

    這種校驗(yàn)方式非常容易理解,所有的代碼都需要程序員自己控制,相當(dāng)靈活。

    但有如下幾個(gè)不方便之處。

    ·用戶(hù)需要書(shū)寫(xiě)大量的校驗(yàn)代碼,使程序變得煩瑣。

    · 數(shù)據(jù)校驗(yàn)應(yīng)該在填充ActionForm里完成,最好能在客戶(hù)端完成校驗(yàn),而不是推遲到Action 里才完成數(shù)據(jù)校驗(yàn)。

    注意:在實(shí)際的使用中,這種校驗(yàn)方式不僅程序開(kāi)發(fā)復(fù)雜,且性能也不高。

     

    結(jié)合commons-validator.jar 的校驗(yàn)

    借助于commons-validator.jar 的支持, Struts的校驗(yàn)功能非常強(qiáng)大,幾乎不需書(shū)寫(xiě)任何代碼。不僅可以完成服務(wù)器端校驗(yàn),同時(shí)還可完成客戶(hù)端校驗(yàn),即彈出Javascript 提示。

    使用commons-validator.jar 校驗(yàn)框架時(shí),有如下幾個(gè)通用配置。

    ·增加校驗(yàn)資源。

    ·利用ValidatorPlugIn 加載校驗(yàn)資源。

    ·ActionForm 使用ValidatorForm 的于類(lèi)。

    下面分別通過(guò)三個(gè)示例講解這三種校驗(yàn):基本的校驗(yàn)、對(duì)動(dòng)態(tài)ActionForm 執(zhí)行校驗(yàn)及彈出JavaScript 校驗(yàn)提示。

     

    1. 繼承ValidatorForm 的校驗(yàn)

    如果需要使用commons-validator 框架,請(qǐng)按如下步驟進(jìn)行。

    (1) Struts 的ActionForm必須是ValidatorForm的子類(lèi),提供驗(yàn)證屬性字段的getter、setter方法

    (2) 編寫(xiě)表單域時(shí)必須滿(mǎn)足校驗(yàn)規(guī)則。校驗(yàn)規(guī)則都由規(guī)則文件控制,規(guī)則文件有以下兩個(gè)。

    * validator-rules.xml 文件

    * validation.xml 文件

    第一個(gè)文件可在Struts 的解壓縮后的文件夾的lib 下找到,將該文件復(fù)制到WEB-INF

    2. common-validator支持的校驗(yàn)規(guī)則

    common-validator支持的校驗(yàn)規(guī)則非常豐富,特別是mask 和validwhen 兩個(gè)規(guī)則,

    極大地豐富了該校驗(yàn)框架的功能。

    常用的校驗(yàn)規(guī)則有如下幾種。

    * required: 必填。

    * va1idwhen: 必須滿(mǎn)足某個(gè)有效條件。

    * minlength: 輸入必須大于最小長(zhǎng)度。

    * maxlength: 輸入必須小于最大長(zhǎng)度。

    * mask: 輸入匹配正確的表達(dá)式。

    * byte: 輸入只能是一個(gè)byte 類(lèi)型變量。

    * short: 輸入只能是一個(gè)short 類(lèi)型變量。

    * integer: 輸入只能是一個(gè)integer 變量。

    * long: 輸入只能是一個(gè)long 變量。

    * float: 輸入只能是一個(gè)float 變量。

    * double: 輸入只能是一個(gè)double 變量。

    * date: 輸入必須是一個(gè)日期。

    * intRange: 輸入的數(shù)字必須在整數(shù)范圍內(nèi)。

    * floatRange: 輸入的數(shù)字必須在單精度浮點(diǎn)數(shù)范圍內(nèi)。

    * doubleRange: 輸入的數(shù)字必須在雙精度浮點(diǎn)數(shù)范圍內(nèi)。

    * email: 輸入必須是有效的E-mail 地址。

    * uri: 輸入必須是有效的uri 地址。

    3.使用DynaValidatorForm 的校驗(yàn)

    即使不書(shū)寫(xiě)ActionForm,也可以利用cornmon-validator 校驗(yàn)框架。此時(shí)使用的ActionForm 的實(shí)現(xiàn)類(lèi),必須既是動(dòng)態(tài)Form ,也是驗(yàn)證Form,DynaValidatorForm 就是滿(mǎn)足這兩個(gè)條件的Form。

    4. 彈出客戶(hù)端JavaScript提示

    如需要彈出客戶(hù)端JavaScript 校驗(yàn)非常簡(jiǎn)單,無(wú)須修改其他配置文件,只需修改登錄使用的JSP 頁(yè)面的兩個(gè)地方。

    (1) 為form 元素增加onsubmit="return validateXxxForm(this);"屬性,其中的XxxForm就是需要校驗(yàn)的form 名,與struts-config.xrnl中配置的form-bean 的name 屬性一致,也與validation.xrnl文件中需要校驗(yàn)的form 的name 屬性一致。

    (2) 增加<html:javascript formName="xxxForm"/> ,其中xxxForm 是需要校驗(yàn)的form 名。

    注意:即使使用了客戶(hù)端技驗(yàn)規(guī)則,也不要?jiǎng)h除頁(yè)面的htm1 :rnessages 標(biāo)簽。因?yàn)樵摌?biāo)簽會(huì)在客戶(hù)端技驗(yàn)通過(guò),而在服務(wù)器端技驗(yàn)并未通過(guò)時(shí)彈出提示。

     

    Struts 的異常框架

    Struts 1.1 版本中加入了對(duì)異常的處理,稱(chēng)之為Exception Handling,標(biāo)志著作為一個(gè)整體的框架, Struts 越來(lái)越趨于成熟。

    在以前的Struts 開(kāi)發(fā)過(guò)程中,對(duì)于異常的處理,主要是采用手動(dòng)處理的方式:如通過(guò)try/catch 等捕獲異常:然后將定制個(gè)性化的,比較詳細(xì)的錯(cuò)誤信息放進(jìn)ActionMessage中:最后在返回頁(yè)面中把這些錯(cuò)誤信息反饋給用戶(hù)。

    對(duì)于異常的原始信息,不管是最終用戶(hù)還是開(kāi)發(fā)員都不希望看到。

    借助于Struts 的異常框架,異常處理只需通過(guò)struts-config.xm1文件定義即可。根據(jù)異常定義的位置不同,異常可分為局部異常和全局異常兩種。

    ·局部異常作為action 的子元素中定義。

    ·全局異常在globa1-excetpions 元素中定義。

    異常定義的格式如下:

    <exception key="keyNarne" type="ExceptionNarne" scope="scope" path="uri"/>: 當(dāng)Struts 出現(xiàn)ExceptionNarne 的異常時(shí),頁(yè)面自動(dòng)轉(zhuǎn)向uri 指向的資源,并在該頁(yè)面輸出keyName 對(duì)應(yīng)的國(guó)際化中的出錯(cuò)提示。

    幾種常用的Action

    除了基本的Action 之外, Struts 還提供了幾個(gè)其他類(lèi)型的Action ,這些Action 大大豐富了Struts 的功能。下面介紹如下兒個(gè)常用的Action 。

    * DispatchAction: 能同時(shí)完成多個(gè)Action 功能的Action 。

    * ForwardActon: 該類(lèi)用來(lái)整合Struts 和其他業(yè)務(wù)邏輯組件,通常只對(duì)請(qǐng)求作有效性檢查。

    * IncludeAction: 用于引入其他的資源和頁(yè)面。

    * LookupDispatchAction: DispatchAction 的子類(lèi),根據(jù)按鈕的key ,控制轉(zhuǎn)發(fā)給action的方法。

    * MappingDispatchAction: DispatchAction 的子類(lèi),一個(gè)action 可映射出多個(gè)Action地址。

    * SwitchAction: 用于從一個(gè)模塊轉(zhuǎn)換至另一個(gè)模塊,如果應(yīng)用分成多個(gè)模塊時(shí),就可以使用SwitchAction 完成模塊之間的切換。

     

    DispatchAction

    在該action 的配置中,增加了parameter屬性,該屬性用于指定參數(shù)名,即Struts 將根據(jù)該參數(shù)的值調(diào)用對(duì)應(yīng)的方法。為了讓請(qǐng)求增加method 的參數(shù),method參數(shù)對(duì)應(yīng)的是要請(qǐng)求執(zhí)行的方法。

    <action path="/login" type="com.hoo.LoginAction" name="loginForm" scope="request" validate="true" input="login.jsp" parameter="method">

         <forward name="success" path="/welcome.jsp"/>

    </action>

    Login.do?method=login

     

    MappingDispatchAction

    可將同一個(gè)Action 的不同方法映射成多個(gè)Action URI ,這種Action 的寫(xiě)法與DispatchAction 非常相似,同樣不需要重寫(xiě)execute 方法,而是將書(shū)寫(xiě)多個(gè)自定義的方法。這些方法除了方法名與execute 方法不同外,其他的參數(shù)列表及異常處理完全一樣。

    <!-- 配置第一個(gè)Action. 實(shí)現(xiàn)類(lèi)是com.hoo.LoginAction , parameter 為add-->

    <action path="/add" type="com.hoo.LoginAction" name="loginForm" scope="request" validate="true" input="login.jsp" parameter="add">

         <forward name="success" path="/welcome.jsp"/>

    </action>

    <! 配置第二個(gè)Action. 實(shí)現(xiàn)類(lèi)是com.hoo.LoginAction , parameter 為modify-->

    <action path="/modify" type="com.hoo.LoginAction" name="loginForm" scope="request" validate="true" input="login.jsp" parameter="modify">

         <forward name="success" path="/welcome.jsp"/>

    </action>

    其中,path對(duì)應(yīng)的是請(qǐng)求的地址uri,而parameter是對(duì)于當(dāng)前請(qǐng)求所執(zhí)行的方法;

    注意:使用MappingDispatchAction 并沒(méi)有帶來(lái)太大的優(yōu)勢(shì),系統(tǒng)完全可以書(shū)寫(xiě)兩個(gè)Action,分別定義兩個(gè)不同的action 映射,而其他部分沒(méi)有區(qū)別。

    LookupDispatchAction

    LookupDispatchAction也是DispatchAction 的一種,但它的處理更加簡(jiǎn)單。該Action也可包含多個(gè)處理方法,它可讓處理方法與按鈕直接關(guān)聯(lián),無(wú)須使用任何的JavaScript腳本。因此可通過(guò)重寫(xiě)getKeyMethodMap方法完成按鈕與Action 中方法的關(guān)聯(lián)。

    //用于關(guān)聯(lián)按鈕和方法

    protected Map getKeyMethodMap() {

         Map map = new HashMap();

         //如果按鈕標(biāo)題的key 為button.add. 則提交該按鈕時(shí)對(duì)應(yīng)add 方法

         map .put ("button. add" , "add");

         //如果按鈕標(biāo)題的key 為button.modify. 則提交該按鈕時(shí)對(duì)應(yīng)modify 方法

         map.put ("button.modify" , "modify") ;

         return map;

    }

     

    ForwardAction

    如果需要從一個(gè)頁(yè)面或資源轉(zhuǎn)換到另一個(gè)資源時(shí),直接使用頁(yè)面或資源路徑的超級(jí)鏈接定位并不是好的做法,這使得控制器沒(méi)有機(jī)會(huì)處理相關(guān)的請(qǐng)求事直。

    使用ForwardAction可以完成請(qǐng)求的轉(zhuǎn)發(fā),當(dāng)控制器調(diào)用ForwardAction的perform()方法時(shí),它會(huì)使用屬性parameter 所設(shè)定的路徑進(jìn)行forward 的動(dòng)作。下面是一個(gè)設(shè)定ForwardAction的例子:

    <actlon-mapplngs>

         <action path="/welcome" type="org.apache.struts.actions.ForwardAction" parameter="/welcome.jsp"/>

    </action-mappings>

    該action 僅僅完成轉(zhuǎn)發(fā),并沒(méi)有執(zhí)行其他的額外動(dòng)作。頁(yè)面控制轉(zhuǎn)發(fā)的代碼如下:

    <a href="welcome.do">轉(zhuǎn)入</a>

    當(dāng)單擊轉(zhuǎn)入超級(jí)鏈接時(shí),將可以轉(zhuǎn)向ForwardAction中parameter指向的資源。

     

    IncludeAction

    IncludeAction的用法與ForwardAction的用法比較相似,區(qū)別在于ForwardAction將跳轉(zhuǎn)到action 定義的資源,而IncludeAction用于引入該action 對(duì)應(yīng)的資源。

    下面是IncludeAction定義的源代碼:

    <action-mapplngs>

         <action path="/welcome" type="org.apache. struts.actions.IncludeAction" parameter="/welcome.jsp"/>

    </action-mappings>

    該action 用于經(jīng)welcome.jsp 作為資源導(dǎo)入。

    頁(yè)面中負(fù)責(zé)加載該action 所導(dǎo)入資源的代碼如下:

    <jsp:include page="welcome.do"/><br>

    上面的代碼將會(huì)把welcome action 定義的資源導(dǎo)入該頁(yè)面。

     

    SwitchAction

    SwitchAction 主要用于模塊之間的切換。當(dāng)一個(gè)應(yīng)用之中存在多個(gè)模塊時(shí),使用SwitchAction在不同模塊之間的action 之間切換還是相當(dāng)方便的。

     

    Struts 的常見(jiàn)擴(kuò)展方法

    Struts 的強(qiáng)大吸引力還來(lái)自于它的可擴(kuò)展性,其擴(kuò)展性通常有如下三種方式。

    ·實(shí)現(xiàn)PlugIn: 如果需要在應(yīng)用啟動(dòng)或關(guān)閉時(shí)完成某些操作,可以創(chuàng)建自己的PlugIn類(lèi)。

    ·繼承RequestProcessor: 如果想在請(qǐng)求被處理中的某個(gè)時(shí)刻做一些業(yè)務(wù)邏輯時(shí),可以考慮實(shí)現(xiàn)自己的RequestProcessor 類(lèi)。

    ·繼承ActionServlet: 如果需要在每次開(kāi)始處理請(qǐng)求之前,或者處理請(qǐng)求結(jié)束之后完成某些操作,可以實(shí)現(xiàn)自己的ActionServlet 來(lái)完成擴(kuò)展。

     

    下面分別從三個(gè)方面來(lái)介紹Struts 的擴(kuò)展。

    實(shí)現(xiàn)PlugIn 接口

    Struts 已經(jīng)演示了PlugIn 的擴(kuò)展方法:與common- validation 的整合。后面還將介紹Spring 與Struts 的整合,也利用了PlugIn 的擴(kuò)展。

    在下面的應(yīng)用中,系統(tǒng)使用Hibernate 作為持久層,在啟動(dòng)時(shí)創(chuàng)建SessionFactory 實(shí)例,并將該SessionFactory 存入application ,在應(yīng)用關(guān)閉時(shí)銷(xiāo)毀SessionFactory 。只需如下兩步即可完成此功能。

    (1) 實(shí)現(xiàn)自己的PlugIn 類(lèi)。

         實(shí)現(xiàn)PlugIn 接口必須實(shí)現(xiàn)如下兩個(gè)方法。

         1 void destroy()。

         2 void init(ActionServlet serlet, ModuleConfig config) 。

         應(yīng)用啟動(dòng)時(shí)調(diào)用init 方法,而應(yīng)用關(guān)閉時(shí)則調(diào)用destroy 方法。

         下面是SessionFactoryLoaderPlugIn 的實(shí)現(xiàn)類(lèi):

         public class SessionFactoryLoaderPlugin implements PlugIn {

              //Hibernate 的配置文件

              private String configFile;

              //應(yīng)用關(guān)閉時(shí),銷(xiāo)毀資源

              public void destroy()

                   System.out.println("系統(tǒng)銷(xiāo)毀SessionFactory");

              }

              //應(yīng)用啟動(dòng)時(shí),完成SessionFactory 的初始化

              public void init(ActionServlet actionServlet , ModuleConfig config) throws ServletException

                   System.out.println("系統(tǒng)以" + getConfigFile() + "為配置文件初始化SessionFactory") ;

                  //獲取Plugln 配置文件的方法

                   public String getConfigFile() {

                        return configFile;

                   }

              // 負(fù)責(zé)加載Plugln 配置屬性的方法

              public void setConfigFile(String configFile) {

                    this.configFile = configFile;

              }

         }

    在上面的PlugIn 中,并沒(méi)有真正初始化SessionFactory ,僅在控制臺(tái)打印出字符串來(lái)標(biāo)識(shí)創(chuàng)建動(dòng)作。另外,還提供了configFile 屬性的setter 和getter 方法,這兩個(gè)方法負(fù)責(zé)訪(fǎng)問(wèn)plugin 元素的configFile 屬性。

     

    ( 2 ) 將SessionFactoryLoaderPlugIn 配置在struts-config.xml 文件中。方法與ValidatorPlugIn 的配置并沒(méi)有區(qū)別,下面是配置SessionFactoryLoaderPlugIn 的代碼:

    <plug-in className="hoo.SessionFactoryLoaderPluging">

         <set-property property="conf工gFile" value=" WEB-INF/hibernate.cfg.xml"I>

    </plug-in>

    在配置SessionFactoryLoaderPlugIn 時(shí),配置了configFile 屬性,該屬性用于確定Hibernate 配置文件的文件名。

     

    繼承RequestProcessor

    RequestProcessor 是Struts 的核心類(lèi),而Struts 的核心控制器是ActionServlet 。但ActionServlet 并未完成真正的處理,只是調(diào)用RequestProcessor , RequestProcessor 才是Struts 的核心類(lèi)。

    擴(kuò)展RequestProcessor 的實(shí)例在Spring 中有個(gè)示范, Spring 提供的Delegating RequestProcessor 是一個(gè)很好的示例。下面示例對(duì)RequestProcessor 進(jìn)行簡(jiǎn)單的擴(kuò)展。

    RequestProcessor 包含了如下常見(jiàn)的方法。

    * ActionForm processActionForm: RequestProcessor填充ActionForm 時(shí)執(zhí)行該方法。

    * Action processActionCreate: RequestProcessor 調(diào)用Action 時(shí)調(diào)用該方法。

    * boolean processPreprocess: 預(yù)處理用戶(hù)請(qǐng)求時(shí)執(zhí)行該方法。

    * boolean processValidate: 處理輸入校驗(yàn)時(shí)調(diào)用該方法。

    擴(kuò)展RequestProcessor 只需兩步即可。

    (2)在struts-config.xml 文件中配置MyRequestProcessor。用戶(hù)重寫(xiě)了RequestProcessor,但Struts 并不知道,必須在struts-config且nl 中配置才可以。

    下面是配置MyRequestProcessor 的代碼:

    <controller processorClass="lee.MyRequestProcessor" />

    該屬性的配置應(yīng)該放在action-mappings元素之后。

    注意:重寫(xiě)RequestProccessor的方法時(shí),別忘了使用super 來(lái)調(diào)用父類(lèi)的動(dòng)作。如果沒(méi)有調(diào)用該方法,意味著開(kāi)發(fā)者必須完成Struts 框架所完成的動(dòng)作。這是不應(yīng)該的,因?yàn)槌绦騿T只是在框架中加入額外的處理,并不是要替代Struts。

     

    繼承ActionServlet

    如果需要在開(kāi)始處理請(qǐng)求,或者處理結(jié)束之后加入自己的處理時(shí),可對(duì)ActionServlet進(jìn)行擴(kuò)展。例如解決中文的編碼問(wèn)題。

    ActionServlet 接收處理請(qǐng)求參數(shù)時(shí),并不是按GBK 的解碼方式處理請(qǐng)求,因此容易形成亂碼。為了解決該問(wèn)題,可以強(qiáng)制指定ActionServlet 使用GBK 的解碼方式。

    繼承ActionServlet重寫(xiě)process方法,設(shè)置request、response編碼為gbk,然后配置在web.xml中。



    作者:hoojo
    出處:
    blog:http://blog.csdn.net/IBM_hoojo
             http://hoojo.cnblogs.com
    本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。


    版權(quán)所有,轉(zhuǎn)載請(qǐng)注明出處 本文出自:
    分享道版權(quán)所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處,謝謝
    posted on 2012-01-29 16:48 hoojo 閱讀(1940) 評(píng)論(4)  編輯  收藏 所屬分類(lèi): JavaEEStruts

    評(píng)論:
    # re: Struts 筆記 2012-01-30 10:54 | tb
    不錯(cuò) 謝謝分享了   回復(fù)  更多評(píng)論
      
    # re: Struts 筆記 2012-02-01 14:04 | 楊Coder
    總結(jié)得相當(dāng)全面啊!  回復(fù)  更多評(píng)論
      
    # re: Struts 筆記 2012-02-01 16:23 | hoojo
    @楊Coder
    謝謝支持  回復(fù)  更多評(píng)論
      
    # re: Struts 筆記 2012-02-03 23:37 | allenny
    你這不是1.0版本的Struts嘛  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲女人被黑人巨大进入| 亚洲精品视频免费观看| 国产午夜亚洲精品国产成人小说| 亚洲AV美女一区二区三区| 在线综合亚洲欧洲综合网站| A国产一区二区免费入口| 无码国产精品一区二区免费式影视 | 四虎永久免费网站免费观看| 亚洲韩国精品无码一区二区三区 | 亚洲av无码一区二区三区在线播放| 中文字幕无线码中文字幕免费| 永久免费毛片在线播放| 国产精品亚洲片在线| 亚洲精华国产精华精华液| 色欲国产麻豆一精品一AV一免费| 国产免费观看青青草原网站| 亚洲精品乱码久久久久久下载| 在线播放国产不卡免费视频| 特级做A爰片毛片免费69| 亚洲色WWW成人永久网址| 亚洲精品无码你懂的| 91大神在线免费观看| 亚洲国产精品无码久久青草| 国产免费丝袜调教视频| 久久影视综合亚洲| 亚洲欧美aⅴ在线资源| 91精品国产免费入口| 国产亚洲情侣一区二区无| 亚洲精品国产suv一区88| 中文字幕成人免费视频| 亚洲自偷自偷在线制服| 日本系列1页亚洲系列| 亚洲综合免费视频| 亚洲大尺度无码专区尤物| 日韩成人精品日本亚洲| 成人A级毛片免费观看AV网站| 中文字幕亚洲综合久久2| 99久久精品毛片免费播放| 亚洲成av人片天堂网老年人| 亚洲人成网亚洲欧洲无码| 成人免费大片免费观看网站|