青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Shuffy

不斷的學習,不斷的思考,才能不斷的進步.Let's do better together!
posts - 102, comments - 43, trackbacks - 0, articles - 19

Java Annotation 高級應用

Posted on 2008-12-09 15:44 Shuffy 閱讀(832) 評論(0)  編輯 收藏 引用 所屬分類: Java
原文:http://www.matrix.org.cn/resource/article/44/44062_Java+Annotation+Apt.html
前言:
前不久在matrix上先后發表了
《java annotation 入門》《java annotation 手冊》兩篇文章,比較全面的對java annotation的語法、原理、使用三方面進行了闡述。由于《入門》中的簡單例程雖然簡單明了的說明了annotation用法,但給大家的感覺可能是意猶未見,所以在此行文《java annotation高級應用》,具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。

一、摘要:
《java annotation高級應用》具體實例化解釋annotation和annotation processing tool(APT)的使用。望能對各位的有所幫助。本文列舉了用于演示annotation的BRFW演示框架、演示APT的apt代碼實例,并對其進行較為深度的分析,希望大家多多提意見。

二、annotation實例分析
1.BRFW(Beaninfo Runtime FrameWork)定義:
本人編寫的一個annotation功能演示框架。顧名思義,BRFW就是在運行時取得bean信息的框架。

2.BRFW的功能:
A.源代碼級annotation:在bean的源代碼中使用annotation定義bean的信息;
B.運行時獲取bean數據:在運行時分析bean class中的annotation,并將當前bean class中field信息取出,功能類似xdoclet;
C.運行時bean數據的xml綁定:將獲得的bean數據構造為xml文件格式展現。熟悉j2ee的朋友知道,這個功能類似jaxb。

3.BRFW框架:
BRFW主要包含以下幾個類:
A.Persistent類:定義了用于修飾類的固有類型成員變量的annotation。
B.Exportable類:定義了用于修飾Class的類型的annotation。
C.ExportToXml類:核心類,用于完成BRFW的主要功能:將具有Exportable Annotation的bean對象轉換為xml格式文本。
D.AddressForTest類:被A和B修飾過的用于測試目的的地址bean類。其中包含了地址定義所必需的信息:國家、省級、城市、街道、門牌等。
E.AddressListForTest類:被A和B修飾過的友人通訊錄bean類。其中包含了通訊錄所必備的信息:友人姓名、年齡、電話、住址(成員為AddressForTest類型的 ArrayList)、備注。需要說明的是電話這個bean成員變量是由字符串類型組成的ArrayList類型。由于朋友的住址可能不唯一,故這里的住址為由AddressForTest類型組成的ArrayList。
從上面的列表中,可以發現A、B用于修飾bean類和其類成員;C主要用于取出bean類的數據并將其作xml綁定,代碼中使用了E作為測試類;E中可能包含著多個D。
在了解了這個簡單框架后,我們來看一下BRFW的代碼吧!

4.BRFW源代碼分析:
A.Persistent類:
清單1:

package com.bjinfotech.practice.annotation.runtimeframework;

import java.lang.annotation.*;

/**
* 用于修飾類的固有類型成員變量的annotation
* @author cleverpig
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Persistent {
        String value() default "";
}


B.Exportable類:
清單2:

package com.bjinfotech.practice.annotation.runtimeframework;

import java.lang.annotation.*;

/**
* 用于修飾類的類型的annotation
* @author cleverpig
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Exportable {
        //名稱
        String name() default "";
        //描述
        String description() default "";
        //省略name和description后,用來保存name值
        String value() default "";
        
}


C.AddressForTest類:
清單3:

package com.bjinfotech.practice.annotation.runtimeframework;

/**
* 用于測試的地址類
* @author cleverpig
*
*/
@Exportable("address")
public class AddressForTest {
        //國家
        @Persistent
        private String country=null;
        
        //省級
        @Persistent
        private String province=null;
        
        //城市
        @Persistent
        private String city=null;
        
        //街道
        @Persistent
        private String street=null;

        //門牌
        @Persistent
        private String doorplate=null;
        
        public AddressForTest(String country,String province,
                        String city,String street,String doorplate){
                this.country=country;
                this.province=province;
                this.city=city;
                this.street=street;
                this.doorplate=doorplate;
        }
        
}


D.AddressListForTest類:
清單4:

package com.bjinfotech.practice.annotation.runtimeframework;

import java.util.*;

/**
* 友人通訊錄
* 包含:姓名、年齡、電話、住址(多個)、備注
* @author cleverpig
*
*/
@Exportable(name="addresslist",description="address list")
public class AddressListForTest {
        //友人姓名
        @Persistent
        private String friendName=null;
        
        //友人年齡
        @Persistent
        private int age=0;
        
        //友人電話
        @Persistent
        private ArrayList<String> telephone=null;
        
        //友人住址:家庭、單位
        @Persistent
        private ArrayList<AddressForTest> AddressForText=null;
        
        //備注
        @Persistent
        private String note=null;
        
        public AddressListForTest(String name,int age,
                        ArrayList<String> telephoneList,
                        ArrayList<AddressForTest> addressList,
                        String note){
                this.friendName=name;
                this.age=age;
                this.telephone=new ArrayList<String>(telephoneList);
                this.AddressForText=new ArrayList<AddressForTest>(addressList);
                this.note=note;
                
        }
}


E.ExportToXml類:
清單5:

package com.bjinfotech.practice.annotation.runtimeframework;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;

/**
* 將具有Exportable Annotation的對象轉換為xml格式文本
* @author cleverpig
*
*/
public class ExportToXml {
        /**
         * 返回對象的成員變量的值(字符串類型)
         * @param field 對象的成員變量
         * @param fieldTypeClass 對象的類型
         * @param obj 對象
         * @return 對象的成員變量的值(字符串類型)
         */
        private String getFieldValue(Field field,Class fieldTypeClass,Object obj){
                String value=null;
                
                try{
                        if (fieldTypeClass==String.class){
                                value=(String)field.get(obj);
                        }
                        else if (fieldTypeClass==int.class){
                                value=Integer.toString(field.getInt(obj));
                        }
                        else if (fieldTypeClass==long.class){
                                value=Long.toString(field.getLong(obj));
                        }
                        else if (fieldTypeClass==short.class){
                                value=Short.toString(field.getShort(obj));
                        }
                        else if (fieldTypeClass==float.class){
                                value=Float.toString(field.getFloat(obj));
                        }
                        else if (fieldTypeClass==double.class){
                                value=Double.toString(field.getDouble(obj));
                        }
                        else if (fieldTypeClass==byte.class){
                                value=Byte.toString(field.getByte(obj));
                        }
                        else if (fieldTypeClass==char.class){
                                value=Character.toString(field.getChar(obj));
                        }
                        else if (fieldTypeClass==boolean.class){
                                value=Boolean.toString(field.getBoolean(obj));
                        }
                }
                catch(Exception ex){
                        ex.printStackTrace();
                        value=null;
                }
                return value;
        }
        
        /**
         * 輸出對象的字段,當對象的字段為Collection或者Map類型時,要調用exportObject方法繼續處理
         * @param obj 被處理的對象
         * @throws Exception
         */
        public void exportFields(Object obj) throws Exception{
                Exportable exportable=obj.getClass().getAnnotation(Exportable.class);        
                if (exportable!=null){
                        if (exportable.value().length()>0){
//                                System.out.println("Class annotation Name:"+exportable.value());
                        }
                        else{
//                                System.out.println("Class annotation Name:"+exportable.name());
                        }
                }
                else{
//                        System.out.println(obj.getClass()+"類不是使用Exportable標注過的");
                }
                
                //取出對象的成員變量
                Field[] fields=obj.getClass().getDeclaredFields();
                
                for(Field field:fields){
                        //獲得成員變量的標注
                        Persistent fieldAnnotation=field.getAnnotation(Persistent.class);
                        if (fieldAnnotation==null){
                                continue;
                        }
                        //重要:避免java虛擬機檢查對私有成員的訪問權限
                        field.setAccessible(true);
                        Class typeClass=field.getType();
                        String name=field.getName();
                        String value=getFieldValue(field,typeClass,obj);
                        
                        //如果獲得成員變量的值,則輸出
                        if (value!=null){
                                System.out.println(getIndent()+"<"+name+">\n"
                                                +getIndent()+"\t"+value+"\n"+getIndent()+"</"+name+">");
                        }
                        //處理成員變量中類型為Collection或Map
                        else if ((field.get(obj) instanceof Collection)||
                                        (field.get(obj) instanceof Map)){
                                exportObject(field.get(obj));
                        }
                        else{
                                exportObject(field.get(obj));
                        }
                        
                }
        }
        
        //縮進深度
        int levelDepth=0;
        //防止循環引用的檢查者,循環引用現象如:a包含b,而b又包含a
        Collection<Object> cyclicChecker=new ArrayList<Object>();
        
        /**
         * 返回縮進字符串
         * @return
         */
        private String getIndent(){
                String s="";
                for(int i=0;i<levelDepth;i++){
                        s+="\t";
                }
                return s;
        }
        /**
         * 輸出對象,如果對象類型為Collection和Map類型,則需要遞歸調用exportObject進行處理
         * @param obj
         * @throws Exception
         */
        public void exportObject(Object obj) throws Exception{
                Exportable exportable=null;
                String elementName=null;
                
                //循環引用現象處理
                if (cyclicChecker.contains(obj)){
                        return;
                }
                
                cyclicChecker.add(obj);
                
                //首先處理Collection和Map類型
                if (obj instanceof Collection){
                        for(Iterator i=((Collection)obj).iterator();i.hasNext();){
                                exportObject(i.next());
                        }
                }
                else if (obj instanceof Map){
                        for(Iterator i=((Map)obj).keySet().iterator();i.hasNext();){
                                exportObject(i.next());
                        }
                }
                else{

                        exportable=obj.getClass().getAnnotation(Exportable.class);
                        //如果obj已經被Exportable Annotation修飾過了(注意annotation是具有繼承性的),
                        //則使用其name作為輸出xml的元素name
                        if (exportable!=null){
                                if (exportable.value().length()>0){
                                        elementName=exportable.value();
                                }
                                else{
                                        elementName=exportable.name();
                                }
                        }
                        //未被修飾或者Exportable Annotation的值為空字符串,
                        //則使用類名作為輸出xml的元素name
                        if (exportable==null||elementName.length()==0){
                                elementName=obj.getClass().getSimpleName();
                        }
                        //輸出xml元素頭
                        System.out.println(getIndent()+"<"+elementName+">");
                        levelDepth++;
                        //如果沒有被修飾,則直接輸出其toString()作為元素值
                        if (exportable==null){
                                System.out.println(getIndent()+obj.toString());
                        }
                        //否則將對象的成員變量導出為xml
                        else{
                                exportFields(obj);
                        }
                        levelDepth--;
                        //輸出xml元素結尾
                        System.out.println(getIndent()+"</"+elementName+">");
                        
                }
                cyclicChecker.remove(obj);
        }
        
        public static void main(String[] argv){
                try{
                        AddressForTest ad=new AddressForTest("China","Beijing",
                                        "Beijing","winnerStreet","10");
                        
                        ExportToXml test=new ExportToXml();
                        
                        ArrayList<String> telephoneList=new ArrayList<String>();
                        telephoneList.add("66608888");
                        telephoneList.add("66608889");
                        
                        ArrayList<AddressForTest> adList=new ArrayList<AddressForTest>();
                        adList.add(ad);
                        
                        AddressListForTest adl=new AddressListForTest("coolBoy",
                                        18,telephoneList,adList,"some words");
                        
                        test.exportObject(adl);
                }
                catch(Exception ex){
                        ex.printStackTrace();
                }
        }
}


在ExportToXml 類之前的類比較簡單,這里必須說明一下ExportToXml類:此類的核心函數是exportObject和exportFields方法,前者輸出對象的xml信息,后者輸出對象成員變量的信息。由于對象類型和成員類型的多樣性,所以采取了以下的邏輯:

在exportObject方法中,當對象類型為Collection和Map類型時,則需要遞歸調用exportObject進行處理;
而如果對象類型不是Collection和Map類型的話,將判斷對象類是否被Exportable annotation修飾過:
如果沒有被修飾,則直接輸出<對象類名>對象.toString()</對象類名>作為xml綁定結果的一部分;
如果被修飾過,則需要調用exportFields方法對對象的成員變量進行xml綁定。

在exportFields 方法中,首先取出對象的所有成員,然后獲得被Persisitent annotation修飾的成員。在其后的一句:field.setAccessible(true)是很重要的,因為bean類定義中的成員訪問修飾都是private,所以為了避免java虛擬機檢查對私有成員的訪問權限,加上這一句是必需的。接著后面的語句便是輸出<成員名>成員值 </成員名>這樣的xml結構。像在exportObject方法中一般,仍然需要判斷成員類型是否為Collection和Map類型,如果為上述兩種類型之一,則要在exportFields中再次調用exportObject來處理這個成員。

在main方法中,本人編寫了一段演示代碼:建立了一個由單個友人地址類(AddressForTest)組成的ArrayList作為通訊錄類(AddressForTest)的成員的通訊錄對象,并且輸出這個對象的xml綁定,運行結果如下:

清單6:

<addresslist>
        <friendName>
                coolBoy
        </friendName>
        <age>
                18
        </age>
        <String>
                66608888
        </String>
        <String>
                66608889
        </String>
        <address>
                <country>
                        China
                </country>
                <province>
                        Beijing
                </province>
                <city>
                        Beijing
                </city>
                <street>
                        winnerStreet
                </street>
                <doorplate>
                        10
                </doorplate>
        </address>
        <note>
                some words
        </note>
</addresslist>


三、APT實例分析:
1.何謂APT?
根據sun官方的解釋,APT(annotation processing tool)是一個命令行工具,它對源代碼文件進行檢測找出其中的annotation后,使用annotation processors來處理annotation。而annotation processors使用了一套反射API并具備對JSR175規范的支持。
annotation processors處理annotation的基本過程如下:首先,APT運行annotation processors根據提供的源文件中的annotation生成源代碼文件和其它的文件(文件具體內容由annotation processors的編寫者決定),接著APT將生成的源代碼文件和提供的源文件進行編譯生成類文件。
簡單的和前面所講的annotation 實例BRFW相比,APT就像一個在編譯時處理annotation的javac。而且從sun開發者的blog中看到,java1.6 beta版中已將APT的功能寫入到了javac中,這樣只要執行帶有特定參數的javac就能達到APT的功能。

2.為何使用APT?
使用APT主要目的是簡化開發者的工作量,因為APT可以在編譯程序源代碼的同時,生成一些附屬文件(比如源文件、類文件、程序發布描述文字等),這些附屬文件的內容也都是與源代碼相關的。換句話說,使用APT就是代替了傳統的對代碼信息和附屬文件的維護工作。使用過hibernate或者beehive等軟件的朋友可能深有體會。APT可以在編譯生成代碼類的同時將相關的文件寫好,比如在使用beehive時,在代碼中使用annotation聲明了許多 struct要用到的配置信息,而在編譯后,這些信息會被APT以struct配置文件的方式存放。

3.如何定義processor?
A.APT工作過程:
從整個過程來講,首先APT檢測在源代碼文件中哪些annotation存在。然后APT將查找我們編寫的annotation processor factories類,并且要求factories類提供處理源文件中所涉及的annotation的annotation processor。接下來,一個合適的annotation processors將被執行,如果在processors生成源代碼文件時,該文件中含有annotation,則APT將重復上面的過程直到沒有新文件生成。

B.編寫annotation processors:
編寫一個annotation processors需要使用java1.5 lib目錄中的tools.jar提供的以下4個包:
com.sun.mirror.apt: 和APT交互的接口;
com.sun.mirror.declaration: 用于模式化類成員、類方法、類聲明的接口;
com.sun.mirror.type: 用于模式化源代碼中類型的接口;
com.sun.mirror.util: 提供了用于處理類型和聲明的一些工具。

每個processor實現了在com.sun.mirror.apt包中的AnnotationProcessor接口,這個接口有一個名為 “process”的方法,該方法是在APT調用processor時將被用到的。一個processor可以處理一種或者多種annotation類型。
一個processor實例被其相應的工廠返回,此工廠為AnnotationProcessorFactory接口的實現。APT將調用工廠類的getProcessorFor方法來獲得processor。在調用過程中,APT將提供給工廠類一個 AnnotationProcessorEnvironment 類型的processor環境類對象,在這個環境對象中,processor將找到其執行所需要的每件東西,包括對所操作的程序結構的參考,與APT通訊并合作一同完成新文件的建立和警告/錯誤信息的傳輸。

提供工廠類有兩個方式:通過APT的“-factory”命令行參數提供,或者讓工廠類在APT的發現過程中被自動定位(關于發現過程詳細介紹請看http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html)。前者對于一個已知的factory來講是一種主動而又簡單的方式;而后者則是需要在jar文件的META-INF/services目錄中提供一個特定的發現路徑:
在包含factory類的jar文件中作以下的操作:在META-INF/services目錄中建立一個名為 com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8編碼文件,在文件中寫入所有要使用到的factory類全名,每個類為一個單獨行。

4.一個簡單的APT實例分析:
A.實例構成:
Review類:定義Review Annotation;
ReviewProcessorFactory類:生成ReviewProcessor的工廠類;
ReviewProcessor類:定義處理Review annotation的Processor;
ReviewDeclarationVisitor類:定義Review annotation聲明訪問者,ReviewProcessor將要使用之對Class進行訪問。
runapt.bat:定義了使用自定義的ReviewProcessor對Review類源代碼文件進行處理的APT命令行。

B.Review類:
清單7:

package com.bjinfotech.practice.annotation.apt;

/**
* 定義Review Annotation
* @author cleverpig
*
*/
public @interface Review {
        public static enum TypeEnum{EXCELLENT,NICE,NORMAL,BAD};
        TypeEnum type();
        String name() default "Review";
}


C.ReviewProcessorFactory類:
清單8:

package com.bjinfotech.practice.annotation.apt;

import java.util.Collection;
import java.util.Set;
import java.util.Arrays;
import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
//請注意為了方便,使用了靜態import
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.emptySet;

/**
* 生成ReviewProcessor的工廠類
* @author cleverpig
*
*/
public class ReviewProcessorFactory implements AnnotationProcessorFactory{
        /**
         * 獲得針對某個(些)類型聲明定義的Processor
         * @param atds 類型聲明集合
         * @param env processor環境
         */
        public AnnotationProcessor getProcessorFor(
                        Set<AnnotationTypeDeclaration> atds,
                        AnnotationProcessorEnvironment env){
                return new ReviewProcessor(env);
        }
        /**
         * 定義processor所支持的annotation類型
         * @return processor所支持的annotation類型的集合
         */
        public Collection<String>         supportedAnnotationTypes(){
                //“*”表示支持所有的annotation類型
                //當然也可以修改為“foo.bar.*”、“foo.bar.Baz”,來對所支持的類型進行修飾
            return unmodifiableCollection(Arrays.asList("*"));
    }
        
        /**
         * 定義processor支持的選項
         * @return processor支持選項的集合
         */
        public Collection<String>         supportedOptions(){
                //返回空集合
            return emptySet();
    }
        
        public static void main(String[] argv){
                System.out.println("ok");
        }
}


D.ReviewProcessor類:
清單9:

package com.bjinfotech.practice.annotation.apt;

import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.util.DeclarationVisitors;
import com.sun.mirror.util.DeclarationVisitor;

/**
* 定義Review annotation的Processor
* @author cleverpig
*
*/
public class ReviewProcessor implements AnnotationProcessor{
        //Processor所工作的環境
        AnnotationProcessorEnvironment env=null;
        
        /**
         * 構造方法
         * @param env 傳入processor環境
         */
        public ReviewProcessor(AnnotationProcessorEnvironment env){
                this.env=env;
        }
        
        /**
         * 處理方法:查詢processor環境中的類型聲明,
         */
        public void process(){
                //查詢processor環境中的類型聲明
                for(TypeDeclaration type:env.getSpecifiedTypeDeclarations()){
                        //返回對類進行掃描、訪問其聲明時使用的DeclarationVisitor,
                        //傳入參數:new ReviewDeclarationVisitor(),為掃描開始前進行的對類聲明的處理
                        //        DeclarationVisitors.NO_OP,表示在掃描完成時進行的對類聲明不做任何處理
                        DeclarationVisitor visitor=DeclarationVisitors.getDeclarationScanner(
                                        new ReviewDeclarationVisitor(),DeclarationVisitors.NO_OP);
                        //應用DeclarationVisitor到類型
                        type.accept(visitor);
                }
        }
}


E.ReviewDeclarationVisitor類:
清單10:

package com.bjinfotech.practice.annotation.apt;

import com.sun.mirror.util.*;
import com.sun.mirror.declaration.*;

/**
* 定義Review annotation聲明訪問者
* @author cleverpig
*
*/
public class ReviewDeclarationVisitor extends SimpleDeclarationVisitor{
        /**
         * 定義訪問類聲明的方法:打印類聲明的全名
         * @param cd 類聲明對象
         */
        public void visitClassDeclaration(ClassDeclaration cd){
                System.out.println("獲取Class聲明:"+cd.getQualifiedName());
        }
        
        public void visitAnnotationTypeDeclaration(AnnotationTypeDeclaration atd){
                System.out.println("獲取Annotation類型聲明:"+atd.getSimpleName());
        }
        
        public void visitAnnotationTypeElementDeclaration(AnnotationTypeElementDeclaration aed){
                System.out.println("獲取Annotation類型元素聲明:"+aed.getSimpleName());
        }
}


F.runapt.bat文件內容如下:
清單11:

E:
rem 項目根目錄
set PROJECT_ROOT=E:\eclipse3.1RC3\workspace\tigerFeaturePractice
rem 包目錄路徑
set PACKAGEPATH=com\bjinfotech\practice\annotation\apt
rem 運行根路徑
set RUN_ROOT=%PROJECT_ROOT%\build
rem 源文件所在目錄路徑
set SRC_ROOT=%PROJECT_ROOT%\test
rem 設置Classpath
set CLASSPATH=.;%JAVA_HOME%;%JAVA_HOME%/lib/tools.jar;%RUN_ROOT%

cd %SRC_ROOT%\%PACKAGEPATH%
apt -nocompile -factory com.bjinfotech.practice.annotation.apt.ReviewProcessorFactory  ./*.java


四、參考資源:
http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品国产成人| 久久久久综合| 欧美不卡视频一区| 亚洲一区二区在线看| 亚洲一区二区在线视频| 精东粉嫩av免费一区二区三区| 欧美激情1区2区3区| 欧美日韩一区国产| 激情久久久久久久| 亚洲一区视频| 久久成人精品无人区| 亚洲精品欧洲| 亚洲欧美色婷婷| 亚洲精品乱码久久久久久按摩观| 亚洲一区二区三区激情| 亚洲黄色高清| 午夜影院日韩| 亚洲视频999| 鲁大师影院一区二区三区| 亚洲欧美日韩国产成人精品影院| 猫咪成人在线观看| 久久精品在线视频| 欧美日韩在线视频一区| 欧美成人资源| 国产在线欧美日韩| 一区二区三区欧美激情| 亚洲人成在线观看网站高清| 欧美一区二区视频在线观看2020 | 久久国产直播| 欧美日韩第一区日日骚| 男人的天堂亚洲在线| 国产视频欧美视频| 亚洲无线一线二线三线区别av| 亚洲黄色三级| 欧美在线free| 欧美一区二区三区日韩视频| 欧美日韩国产一区二区| 亚洲欧洲在线看| 伊人春色精品| 久久不见久久见免费视频1| 亚洲欧美成人一区二区在线电影 | 老司机免费视频久久| 国产伦精品一区二区三区高清| 日韩午夜精品| 一区二区三区高清| 欧美日韩国产精品专区| 91久久精品国产91性色tv| 亚洲精品久久久久| 欧美国产精品v| 亚洲国产精品va| 99视频精品| 欧美全黄视频| 99re8这里有精品热视频免费 | 久久精品一区二区三区不卡牛牛| 欧美一区二区在线观看| 国产精品久久久久久久一区探花| 99视频在线观看一区三区| 在线亚洲精品| 国产精品初高中精品久久| 亚洲少妇在线| 欧美一区视频在线| 国产自产精品| 久久夜色精品国产| 亚洲二区在线观看| 在线一区二区三区做爰视频网站 | 亚洲国产欧美在线| avtt综合网| 国产精品wwwwww| 黄页网站一区| 亚洲欧洲精品成人久久奇米网| 亚洲精品美女久久久久| 欧美日韩系列| 午夜精品久久久99热福利| 久久久另类综合| 在线观看日韩国产| 欧美美女bbbb| 午夜精品久久久久久久男人的天堂 | 亚洲欧美日韩国产一区二区| 国产美女精品视频免费观看| 亚洲男人的天堂在线aⅴ视频| 欧美一区不卡| 在线观看国产一区二区| 欧美精品九九| 欧美在线观看一区| 亚洲风情亚aⅴ在线发布| 亚洲欧美精品伊人久久| 在线播放日韩| 欧美性猛交99久久久久99按摩| 午夜精品视频网站| 亚洲高清激情| 欧美在线一区二区| 亚洲精品乱码久久久久久按摩观 | 亚洲人成网站精品片在线观看| 亚洲一区二区毛片| 国内久久精品视频| 欧美日韩一区二区三区四区五区| 午夜精品美女久久久久av福利| 欧美风情在线观看| 午夜亚洲福利在线老司机| 亚洲国产高清在线| 国产伦理一区| 欧美日韩另类在线| 久久一区二区精品| 欧美一级黄色网| 夜夜夜久久久| 欧美激情精品久久久久久蜜臀| 欧美一区二区三区成人| 99re6这里只有精品视频在线观看| 国产精品伊人日日| 欧美噜噜久久久xxx| 久久久久久亚洲综合影院红桃| 在线亚洲成人| 亚洲激情另类| 欧美91精品| 久久久久久久尹人综合网亚洲| 亚洲一区二区在| 亚洲伦理在线| 亚洲国产精品高清久久久| 国产亚洲va综合人人澡精品| 欧美三日本三级三级在线播放| 欧美**人妖| 久久在线91| 久久精品国产综合精品| 亚洲男女毛片无遮挡| 亚洲视频精品在线| 一区二区三区欧美成人| 亚洲美女区一区| 亚洲毛片一区二区| 最新日韩精品| 亚洲人成在线播放| 亚洲国产老妈| 欧美激情亚洲精品| 欧美电影免费观看高清| 蜜桃av一区二区三区| 久久夜色撩人精品| 麻豆91精品91久久久的内涵| 久久精品九九| 国产精品揄拍一区二区| 中文亚洲欧美| 在线亚洲观看| 亚洲一区综合| 99在线精品视频在线观看| 亚洲精品日韩激情在线电影| 欧美激情五月| 亚洲精品国精品久久99热| 最新中文字幕一区二区三区| 亚洲精品国产欧美| 亚洲另类在线一区| 在线一区二区三区四区五区| 亚洲天堂免费观看| 亚洲影院高清在线| 先锋亚洲精品| 久久精品视频在线看| 久久一区二区精品| 欧美精品三级| 国产精品第十页| 国产美女精品人人做人人爽| 国内精品久久久久久久97牛牛| 激情视频一区二区| 亚洲精品国产精品国自产观看浪潮 | 日韩视频在线观看一区二区| 日韩一区二区久久| 亚洲欧美成aⅴ人在线观看| 欧美一区二区日韩一区二区| 久久乐国产精品| 欧美激情一区在线观看| 亚洲毛片网站| 午夜久久美女| 久久综合久久久久88| 欧美精品三级| 国产日韩欧美制服另类| 一区二区三区在线免费播放| 亚洲激情在线观看| 亚洲一区二区三区精品视频| 久久精品国产亚洲a| 欧美国产日韩亚洲一区| 亚洲久色影视| 亚洲欧美视频在线| 免费观看一级特黄欧美大片| 欧美色精品天天在线观看视频| 国产欧美日韩视频一区二区三区 | 欧美专区在线播放| 欧美连裤袜在线视频| 国产麻豆精品在线观看| 亚洲肉体裸体xxxx137| 午夜精品在线观看| 欧美成人亚洲| 亚洲影视在线播放| 蜜臀久久99精品久久久画质超高清| 欧美日韩久久精品| 一区精品在线播放| 亚洲免费影院| 亚洲高清色综合| 亚洲欧美日韩中文播放| 欧美成人免费在线观看| 国产亚洲制服色| 亚洲一级高清| 亚洲电影av在线| 欧美在线资源| 国产精品国产|