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

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

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

    Johnny's Collections

    生活總是有太多的無奈與失望,讓我們以在努力學習和工作中獲得的成就感和快樂來沖淡它們。

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      10 Posts :: 0 Stories :: 80 Comments :: 0 Trackbacks

    上一篇文章作為一個引子,說明了領域驅動設計的優勢,從本篇文章開始,筆者將會結合自己的實際經驗,談及領域驅動設計的應用。本篇文章主要討論一下我們經常會用到的一些對象:VODTODOPO

    由于不同的項目和開發人員有不同的命名習慣,這里我首先對上述的概念進行一個簡單描述,名字只是個標識,我們重點關注其概念:

     

    概念:

    VOView Object):視圖對象,用于展示層,它的作用是把某個指定頁面(或組件)的所有數據封裝起來。

    DTOData Transfer Object):數據傳輸對象,這個概念來源于J2EE的設計模式,原來的目的是為了EJB的分布式應用提供粗粒度的數據實體,以減少分布式調用的次數,從而提高分布式調用的性能和降低網絡負載,但在這里,我泛指用于展示層與服務層之間的數據傳輸對象。

    DODomain Object):領域對象,就是從現實世界中抽象出來的有形或無形的業務實體。

    POPersistent Object):持久化對象,它跟持久層(通常是關系型數據庫)的數據結構形成一一對應的映射關系,如果持久層是關系型數據庫,那么,數據表中的每個字段(或若干個)就對應PO的一個(或若干個)屬性。

     

    模型:

           下面以一個時序圖建立簡單模型來描述上述對象在三層架構應用中的位置


     

          

    l         用戶發出請求(可能是填寫表單),表單的數據在展示層被匹配為VO

    l         展示層把VO轉換為服務層對應方法所要求的DTO,傳送給服務層。

    l         服務層首先根據DTO的數據構造(或重建)一個DO,調用DO的業務方法完成具體業務。

    l         服務層把DO轉換為持久層對應的PO(可以使用ORM工具,也可以不用),調用持久層的持久化方法,把PO傳遞給它,完成持久化操作。

    l         對于一個逆向操作,如讀取數據,也是用類似的方式轉換和傳遞,略。

     

    VODTO的區別

           大家可能會有個疑問(在筆者參與的項目中,很多程序員也有相同的疑惑):既然DTO是展示層與服務層之間傳遞數據的對象,為什么還需要一個VO呢?對!對于絕大部分的應用場景來說,DTOVO的屬性值基本是一致的,而且他們通常都是POJO,因此沒必要多此一舉,但不要忘記這是實現層面的思維,對于設計層面來說,概念上還是應該存在VODTO,因為兩者有著本質的區別,DTO代表服務層需要接收的數據和返回的數據,而VO代表展示層需要顯示的數據。

           用一個例子來說明可能會比較容易理解:例如服務層有一個getUser的方法返回一個系統用戶,其中有一個屬性是gender(性別),對于服務層來說,它只從語義上定義:1-男性,2-女性,0-未指定,而對于展示層來說,它可能需要用“帥哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。說到這里,可能你還會反駁,在服務層直接就返回“帥哥美女”不就行了嗎?對于大部分應用來說,這不是問題,但設想一下,如果需求允許客戶可以定制風格,而不同風格對于“性別”的表現方式不一樣,又或者這個服務同時供多個客戶端使用(不同門戶),而不同的客戶端對于表現層的要求有所不同,那么,問題就來了。再者,回到設計層面上分析,從職責單一原則來看,服務層只負責業務,與具體的表現形式無關,因此,它返回的DTO,不應該出現與表現形式的耦合。

           理論歸理論,這到底還是分析設計層面的思維,是否在實現層面必須這樣做呢?一刀切的做法往往會得不償失,下面我馬上會分析應用中如何做出正確的選擇。

     

    VODTO的應用

           上面只是用了一個簡單的例子來說明VODTO在概念上的區別,本節將會告訴你如何在應用中做出正確的選擇。

           在以下才場景中,我們可以考慮把VODTO二合為一(注意:是實現層面):

    l         當需求非常清晰穩定,而且客戶端很明確只有一個的時候,沒有必要把VODTO區分開來,這時候VO可以退隱,用一個DTO即可,為什么是VO退隱而不是DTO?回到設計層面,服務層的職責依然不應該與展示層耦合,所以,對于前面的例子,你很容易理解,DTO對于“性別”來說,依然不能用“帥哥美女”,這個轉換應該依賴于頁面的腳本(如JavaScript)或其他機制(JSTLELCSS

    l         即使客戶端可以進行定制,或者存在多個不同的客戶端,如果客戶端能夠用某種技術(腳本或其他機制)實現轉換,同樣可以讓VO退隱

     

    以下場景需要優先考慮VODTO并存:

    l         上述場景的反面場景

    l         因為某種技術原因,比如某個框架(如Flex)提供自動把POJO轉換為UI中某些Field時,可以考慮在實現層面定義出VO,這個權衡完全取決于使用框架的自動轉換能力帶來的開發和維護效率提升與設計多一個VO所多做的事情帶來的開發和維護效率的下降之間的比對。

    l         如果頁面出現一個“大視圖”,而組成這個大視圖的所有數據需要調用多個服務,返回多個DTO來組裝(當然,這同樣可以通過服務層提供一次性返回一個大視圖的DTO來取代,但在服務層提供一個這樣的方法是否合適,需要在設計層面進行權衡)。

     

    DTODO的區別

           首先是概念上的區別,DTO是展示層和服務層之間的數據傳輸對象(可以認為是兩者之間的協議),而DO是對現實世界各種業務角色的抽象,這就引出了兩者在數據上的區別,例如UserInfoUser(對于DTODO的命名規則,請參見筆者前面的一篇博文),對于一個getUser方法來說,本質上它永遠不應該返回用戶的密碼,因此UserInfo至少比User少一個password的數據。而在領域驅動設計中,正如第一篇系列文章所說,DO不是簡單的POJO,它具有領域業務邏輯。

     

    DTODO的應用

           從上一節的例子中,細心的讀者可能會發現問題:既然getUser方法返回的UserInfo不應該包含password,那么就不應該存在password這個屬性定義,但如果同時有一個createUser的方法,傳入的UserInfo需要包含用戶的password,怎么辦?在設計層面,展示層向服務層傳遞的DTO與服務層返回給展示層的DTO在概念上是不同的,但在實現層面,我們通常很少會這樣做(定義兩個UserInfo,甚至更多),因為這樣做并不見得很明智,我們完全可以設計一個完全兼容的DTO,在服務層接收數據的時候,不該由展示層設置的屬性(如訂單的總價應該由其單價、數量、折扣等決定),無論展示層是否設置,服務層都一概忽略,而在服務層返回數據時,不該返回的數據(如用戶密碼),就不設置對應的屬性。

           對于DO來說,還有一點需要說明:為什么不在服務層中直接返回DO呢?這樣可以省去DTO的編碼和轉換工作,原因如下:

    l         兩者在本質上的區別可能導致彼此并不一一對應,一個DTO可能對應多個DO,反之亦然,甚至兩者存在多對多的關系。

    l         DO具有一些不應該讓展示層知道的數據

    l         DO具有業務方法,如果直接把DO傳遞給展示層,展示層的代碼就可以繞過服務層直接調用它不應該訪問的操作,對于基于AOP攔截服務層來進行訪問控制的機制來說,這問題尤為突出,而在展示層調用DO的業務方法也會因為事務的問題,讓事務難以控制。

    l         對于某些ORM框架(如Hibernate)來說,通常會使用“延遲加載”技術,如果直接把DO暴露給展示層,對于大部分情況,展示層不在事務范圍之內(Open session in view在大部分情況下不是一種值得推崇的設計),如果其嘗試在Session關閉的情況下獲取一個未加載的關聯對象,會出現運行時異常(對于Hibernate來說,就是LazyInitiliaztionException)。

    l         從設計層面來說,展示層依賴于服務層,服務層依賴于領域層,如果把DO暴露出去,就會導致展示層直接依賴于領域層,這雖然依然是單向依賴,但這種跨層依賴會導致不必要的耦合。

     

    對于DTO來說,也有一點必須進行說明,就是DTO應該是一個“扁平的二維對象”,舉個例子來說明:如果User會關聯若干個其他實體(例如AddressAccountRegion等),那么getUser()返回的UserInfo,是否就需要把其關聯的對象的DTO都一并返回呢?如果這樣的話,必然導致數據傳輸量的大增,對于分布式應用來說,由于涉及數據在網絡上的傳輸、序列化和反序列化,這種設計更不可接受。如果getUser除了要返回User的基本信息外,還需要返回一個AccountIdAccountNameRegionIdRegionName,那么,請把這些屬性定義到UserInfo中,把一個“立體”的對象樹“壓扁”成一個“扁平的二維對象”,筆者目前參與的項目是一個分布式系統,該系統不管三七二十一,把一個對象的所有關聯對象都轉換為相同結構的DTO對象樹并返回,導致性能非常的慢。

     

     

    DOPO的區別

           DOPO在絕大部分情況下是一一對應的,PO是只含有get/set方法的POJO,但某些場景還是能反映出兩者在概念上存在本質的區別:

    l         DO在某些場景下不需要進行顯式的持久化,例如利用策略模式設計的商品折扣策略,會衍生出折扣策略的接口和不同折扣策略實現類,這些折扣策略實現類可以算是DO,但它們只駐留在靜態內存,不需要持久化到持久層,因此,這類DO是不存在對應的PO的。

    l         同樣的道理,某些場景下,PO也沒有對應的DO,例如老師Teacher和學生Student存在多對多的關系,在關系數據庫中,這種關系需要表現為一個中間表,也就對應有一個TeacherAndStudentPOPO,但這個PO在業務領域沒有任何現實的意義,它完全不能與任何DO對應上。這里要特別聲明,并不是所有多對多關系都沒有業務含義,這跟具體業務場景有關,例如:兩個PO之間的關系會影響具體業務,并且這種關系存在多種類型,那么這種多對多關系也應該表現為一個DO,又如:“角色”與“資源”之間存在多對多關系,而這種關系很明顯會表現為一個DO——“權限”。

    l         某些情況下,為了某種持久化策略或者性能的考慮,一個PO可能對應多個DO,反之亦然。例如客戶Customer有其聯系信息Contacts,這里是兩個一對一關系的DO,但可能出于性能的考慮(極端情況,權作舉例),為了減少數據庫的連接查詢操作,把CustomerContacts兩個DO數據合并到一張數據表中。反過來,如果一本圖書Book,有一個屬性是封面cover,但該屬性是一副圖片的二進制數據,而某些查詢操作不希望把cover一并加載,從而減輕磁盤IO開銷,同時假設ORM框架不支持屬性級別的延遲加載,那么就需要考慮把cover獨立到一張數據表中去,這樣就形成一個DO對應對個PO的情況。

    l         PO的某些屬性值對于DO沒有任何意義,這些屬性值可能是為了解決某些持久化策略而存在的數據,例如為了實現“樂觀鎖”,PO存在一個version的屬性,這個version對于DO來說是沒有任何業務意義的,它不應該在DO中存在。同理,DO中也可能存在不需要持久化的屬性。

     

    DOPO的應用

           由于ORM框架的功能非常強大而大行其道,而且JavaEE也推出了JPA規范,現在的業務應用開發,基本上不需要區分DOPOPO完全可以通過JPAHibernate Annotations/hbm隱藏在DO之中。雖然如此,但有些問題我們還必須注意:

    l         對于DO中不需要持久化的屬性,需要通過ORM顯式的聲明,如:在JPA中,可以利用@Transient聲明。

    l         對于PO中為了某種持久化策略而存在的屬性,例如version,由于DOPO合并了,必須在DO中聲明,但由于這個屬性對DO是沒有任何業務意義的,需要讓該屬性對外隱藏起來,最常見的做法是把該屬性的get/set方法私有化,甚至不提供get/set方法,但對于Hibernate來說,這需要特別注意,由于Hibernate從數據庫讀取數據轉換為DO時,是利用反射機制先調用DO的空參數構造函數構造DO實例,然后再利用JavaBean的規范反射出set方法來為每個屬性設值,如果不顯式聲明set方法,或把set方法設置為private,都會導致Hibernate無法初始化DO,從而出現運行時異常,可行的做法是把屬性的set方法設置為protected

    l         對于一個DO對應多個PO,或者一個PO對應多個DO的場景,以及屬性級別的延遲加載,Hibernate都提供了很好的支持,請參考Hibnate的相關資料。

     

     

        到目前為止,相信大家都已經比較清晰的了解VODTODOPO的概念、區別和實際應用了。通過上面的詳細分析,我們還可以總結出一個原則:分析設計層面和實現層面完全是兩個獨立的層面,即使實現層面通過某種技術手段可以把兩個完全獨立的概念合二為一,在分析設計層面,我們仍然(至少在頭腦中)需要把概念上獨立的東西清晰的區分開來,這個原則對于做好分析設計非常重要(工具越先進,往往會讓我們越麻木)。第一篇系列博文拋磚引玉,大唱領域驅動設計的優勢,但其實領域驅動設計在現實環境中還是有種種的限制,需要選擇性的使用,正如我在《田七的智慧》博文中提到,我們不能永遠的理想化的去選擇所謂“最好的設計”,在必要的情況下,我們還是要敢于放棄,因為最合適的設計才是最好的設計。本來,系列中的第二篇博文應該是討論領取驅動設計的限制和如何選擇性的使用,但請原諒我的疏忽,下一篇系列博文會把這個主題補上,敬請關注。
    posted on 2010-05-27 00:07 Johnny.Liang 閱讀(38247) 評論(16)  編輯  收藏 所屬分類: 系統設計

    Feedback

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-05-27 11:26 瀟湘振宇
    很好,講解的很詳細。理念終歸是理念,實際應用確實是需要視各各應用場景來定。始終相信“沒有最好的設計,只有最合適的設計”。
    加油:) 繼續期待您的下篇博文。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-06-25 15:41 Aidan
    不需要password?那我請問客戶端要登錄,你這個密碼怎么傳入服務器->DAO進行比較?
    還有前面見你對名字發表了一篇文章,假如我有VO,DTO,DO,PO都有你是怎么命名的,我見你在前面發表了一下不應該使用DTO為后綴之類的。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處[未登錄] 2010-06-25 20:47 Johnny.Liang
    @Aidan
    很好的問題,我逐個回答:
    1.我所說的不需要password,是值在DTO層面,同一個UserInfo(我在一篇博文中建議以***Info命名DTO,意為***信息),其實對于不同的場景,本質上是不同的,例如,我有個注冊用戶的方法,需要傳入UserInfo,那么逐個DTO必須擁有一些用戶認證或隱私信息的屬性,但對于查詢用戶的方法,它返回的也是一個UserInfo,但這個UserInfo,從需求來說,它不應該包含密碼和隱私信息,那么這兩個雖然都是UserInfo,但理論上應該定義為兩個獨立的DTO,但實際上,這樣做帶來的負面影響會更大,因為我在一個系統中可能要為一個實體定義太多的DTO,所以我建議定義一個包羅萬有的DTO,但在返回數據時,如果有些數據不應該返回,就把屬性設置為null即可,當然SOA中的SDO有另外的做法,這里不詳說,本人也不甚精通。

    2.對于VO,DTO,DO,PO的命名,我個人的建議(純屬個人意見),還是不要太過技術化,VO可以叫***View,代表一個”視圖“,DTO用***Info,代表”***的信息“,DO由于是就是業務實體本身,所以直接給它一個釋意名稱,如User、Customer……,至于PO,正如我所說,現在的ORM框架已經可以把這個東西在實現層面通過配置文件(xml)或annonation隱藏起來,所以在實現層面基本不需要存在,如果確實需要的話,我覺得”持久化“本身就是計算機世界的一個非業務領域,這里用技術化命名完全沒有問題,因為它確實是”技術化產物“,在業務領域沒有這個概念。這相信也同樣回答了你在另外一篇博文提出的問題。

    希望我的回答能讓你滿意。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-06-25 23:07 Aidan
    首先非常感謝你這么詳細的回答。還有一個問題。
    你前一篇貼的做法是BO需要與DAO偶合,這個時候我們使用IOC窗口時,你們是怎么來做的呢?從service構造傳過去嗎?  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-06-25 23:08 Aidan
    我們使用IOC容器的時候,你們是如何把DAO對象注入到BO對象里面去的。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-06-26 00:57 Johnny.Liang
    @Aidan
    呵呵,謝謝你的持續關注,你這個問題同樣很好,我也曾經被這個問題困擾,你所說的從構造函數傳入,從技術角度來講,是一種可行的辦法,但我不建議這樣做,原因是:
    1)DO(即你所說的BO)從業務上來講,不應該與DAO有關聯,因為DAO對于業務來說是沒有意義的,它屬于應用方面的東西。DO構造函數只能包括用于構造它的所必須的元素(我后面的博文會提及)。
    2)這樣做會讓DO與你的具體實現耦合,試想,如果某天有某種更好的方法讓你動態注入DAO,意味著你不再需要通過構造函數傳入DAO了,那么你是繼續保留這個參數,還是修改構造函數,從而讓所有調用該構造函數的客戶程序受到影響呢?

    事實上,可行的做法有兩種:
    1)利用Spring提供的“動態注入”方式,這種方式的原理是,通過一個Annotation,告訴Spring容器,這個DO是需要注入對象的,即需要受到SpringIoC容器管理,那么Spring就會利用“編譯時織入”或“啟動時織入”的技術來為DO的Class類織入注入相關對象的代碼,這種技術依賴于AspectJ,前者更加需要在編譯環境加入AspectJ的增量編譯器,這種方案是相對麻煩的,而且我曾經在OSGi中應用,會出現一些不可預知的問題導致無法注入。

    2)在你的應用(通常是Web應用)加載SpringContext的時候,設計一個用于保持SpringContext的ContextHolder,利用某種機制(例如Spring提供的Servlet)來在加載SpringContext的時候,把SpringContext的引用保留在該ContextHolder中,然后在你的DO里面,就可以在實現代碼里面通過ContextHolder提供的靜態方法,獲取到你所需要的Bean了。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處[未登錄] 2010-07-01 14:35 DDD
    我現在有個疑問,就是關于VO是否將頁面的查詢條件字段也包括在內?想聽聽博主的見解。。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-07-01 17:38 Johnny.Liang
    @DDD
    對,但頁面查詢用的VO,與表示某個實體的VO不是同一樣東西,如果按我的博文中合并VO與DTO的說法,舉個例子,User,對應的信息DTO應該是UserInfo,而對應的查詢User的DTO應該是另外一個,通常我用UserSearchCriteria來代替,為什么要把UserInfo和UserSearchCriteria區分開來?你可能認為,UserInfo中大部分屬性,都是可以用于查詢的,所以沒必要弄多一個UserSearchCriteria,但事實上,它們本質上是兩樣東西來的,比如,UserInfo有一個birthday的屬性,但對于查詢來說,系統可能要求查詢某個時間段內出生的User,這個時候,UserSearchCirteria就要定義兩個屬性birthdayFrom,birthdayTo了,所以可以看出,兩者只是“表面上相似”,本質上是兩樣東西,為了設計上的靈活性和擴展性,即使當前情況下兩者的屬性一致,也需要分別進行設計。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-07-23 14:03 isnumeric
    @Johnny.Liang
    請問博主,如果日期范圍查詢的時候,不能直接用PO去調用DAO的方法,因為PO不會有開始之期和結束日期這兩個屬性,HQL語句如果是根據業務邏輯生成的,是否也放在Service層,還是放在DAO里呢?  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2010-08-06 00:05 Johnny.Liang
    @isnumeric
    PO應該是我系列博文所說的DTO,它是實體的信息封裝對象,本質上與“實體的查詢信息封裝對象”是不同的,因此最好提供一個Criteria來封裝查詢屬性。

    HQL絕對不能封裝在Service中,因為HQL涉及數據訪問邏輯,應該封裝在數據訪問層中,業務層可以傳入生成HQL的相關數據,由DAO把這些數據轉換為HQL,但Service絕對不要傳入任何與數據訪問實現細節有關的數據,否則Service就被耦合了。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處[未登錄] 2011-03-04 02:42 Terry
    寫的非常好,我收益匪淺啊,解決了我的不少疑惑,尤其是設計層面和實現層面上的區別。謝謝樓主  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2013-10-18 12:50 老唐
    寫的不錯,一看就是做過n多的項目設計才能夠表達的如此清楚,感謝博主!
    不過我也有一些問題:
    1、我是使用mybatis還操作數據庫的,一般在mapper中直接連表做查詢,這種情況下返回的對象能否直接是DTO,還是說必須返回一個map,到service層在拼裝成DTO?  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2015-06-08 15:56 趙輝
    是級別(大小)的項目需要嚴格遵守思想,我感覺現在中國的環境,讓大家都遵照這種思想,還有很長的路要走。  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2015-07-09 20:56 Mango
    請問驗證(特性)應該加在dto還是vo?  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處 2015-10-27 11:14 silymer
    雖然是很久前的文章了,但讀來仍然獲益匪淺,尤其是下面幾位評論者與博主的互動,更是精彩,非常感謝~!  回復  更多評論
      

    # re: 領域驅動設計系列文章(2)——淺析VO、DTO、DO、PO的概念、區別和用處[未登錄] 2015-12-21 10:00 小朱
    之前看過領域驅動,云里霧里,后來放棄了,但是在后來的工作中出現的一些問題當初沒能理解的問題突然明白了,一直糾結的問題看你文章就想通了,感謝哈。!另外想問問DO中能不能能不能直接持有PO? 就是把PO當做對應DO的一個字段或者屬性,還是直接在DO中定義相應的屬性,然后用轉換的方式把PO的一些屬性賦值給DO的對應屬性?  回復  更多評論
      

    主站蜘蛛池模板: 2022年亚洲午夜一区二区福利| 中文字幕亚洲无线码| 久久不见久久见免费影院www日本 久久WWW免费人成—看片 | 国产成人无码免费视频97 | jizz免费观看视频| 亚洲av无码国产综合专区| 亚洲成人免费电影| 亚洲黄色免费在线观看| 亚洲中文字幕无码专区| 中文字幕的电影免费网站| 久久综合图区亚洲综合图区| 免费一级黄色毛片| 亚洲一区二区三区免费| 亚洲成在人线aⅴ免费毛片| xvideos亚洲永久网址| 精品乱子伦一区二区三区高清免费播放 | 在线免费播放一级毛片| 亚洲国产国产综合一区首页| 最近新韩国日本免费观看| 亚洲日本VA午夜在线影院| 亚洲视频网站在线观看| 国产亚洲福利精品一区| 国产成人精品免费午夜app | 最近高清中文字幕免费| 中文字幕无码日韩专区免费| eeuss草民免费| 曰批全过程免费视频观看免费软件| 亚洲熟妇自偷自拍另欧美| 国产亚洲精品看片在线观看 | 亚洲宅男天堂在线观看无病毒| 在线看无码的免费网站| 久久狠狠躁免费观看2020| 少妇性饥渴无码A区免费 | 九九热久久免费视频| 一本一道dvd在线观看免费视频| 黄页网站在线视频免费| 免费国产黄网站在线看| 农村寡妇一级毛片免费看视频 | 一色屋成人免费精品网站| 国产91免费视频| 国产卡一卡二卡三免费入口|