一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

2021-10-09 01:43krun Java教程

這篇文章主要介紹了Java通過(guò)反射,動(dòng)態(tài)修改注解的某個(gè)屬性值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Java反射動(dòng)態(tài)修改注解的某個(gè)屬性值

昨晚看到一條問(wèn)題,大意是樓主希望可以動(dòng)態(tài)得建立多個(gè)Spring 的定時(shí)任務(wù)。

這個(gè)題目我并不是很熟悉,不過(guò)根據(jù)題目描述和查閱相關(guān)Spring 創(chuàng)建定時(shí)任務(wù)的資料,發(fā)現(xiàn)這也許涉及到通過(guò)Java代碼動(dòng)態(tài)修改注解的屬性值。

今天對(duì)此嘗試了一番,

發(fā)現(xiàn)通過(guò)反射來(lái)動(dòng)態(tài)修改注解的屬性值是可以做到的:

眾所周知,java/lang/reflect這個(gè)包下面都是Java的反射類和工具。

Annotation注解,也是位于這個(gè)包里的。注解自從Java 5.0版本引入后,就成為了Java平臺(tái)中非常重要的一部分,常見(jiàn)的如@Override、@Deprecated。

關(guān)于注解更詳細(xì)的信息和使用方法,網(wǎng)上已經(jīng)有很多資料,這里就不再贅述了。

一個(gè)注解通過(guò)@Retention指定其生命周期,本文所討論的動(dòng)態(tài)修改注解屬性值,建立在@Retention(RetentionPolicy.RUNTIM)這種情況。畢竟這種注解才能在運(yùn)行時(shí)(runtime)通過(guò)反射機(jī)制進(jìn)行操作。

那么現(xiàn)在我們定義一個(gè)@Foo注解,它有一個(gè)類型為String的value屬性,該注解應(yīng)用再Field上:

?
1
2
3
4
5
6
7
8
/**
 * Created by krun on 2017/9/18.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Foo {
    String value();
}

再定義一個(gè)普通的Java對(duì)象Bar,它有一個(gè)私有的String屬性val,并為它設(shè)置屬性值為"fff"的@Foo注解:

?
1
2
3
4
5
public class Bar {
 
    @Foo ("fff")
    private String val;
}

接下來(lái)在main方法中我們來(lái)嘗試修改Bar.val上的@Foo注解的屬性值為"ddd"。

先是正常的獲取注解屬性值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Created by krun on 2017/9/18.
 */
public class Main {
    public static void main(String ...args) throws NoSuchFieldException {
        //獲取Bar實(shí)例
        Bar bar = new Bar();
        //獲取Bar的val字段
        Field field = Bar.class.getDeclaredField("val");
        //獲取val字段上的Foo注解實(shí)例
        Foo foo = field.getAnnotation(Foo.class);
        //獲取Foo注解實(shí)例的 value 屬性值
        String value = foo.value();
        //打印該值
        System.out.println(value); // fff
    }
}

首先,我們要知道注解的值是存在哪里的。

在String value = foo.value();處下斷點(diǎn),我們跑一下可以發(fā)現(xiàn):

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

當(dāng)前棧中有這么幾個(gè)變量,不過(guò)其中有一點(diǎn)很特別:foo,其實(shí)是個(gè)Proxy實(shí)例。

Proxy也是java/lang/reflect下的東西,它的作用是為一個(gè)Java類生成一個(gè)代理,就像這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public interface A {
    String func1();
}
 
public class B implements A {    
    @Override
    public String func1() { //do something ... }    
    public String func2() { //do something ... };
}
 
public static void main(String ...args) {
    B bInstance = new B();    
    B bProxy = Proxy.newProxyInstance(
        B.class.getClassLoader(),    // B 類的類加載器
        B.class.getInterfaces(), // B 類所實(shí)現(xiàn)的接口,如果你想攔截B類的某個(gè)方法,必須讓這個(gè)方法在某個(gè)接口中聲明并讓B類實(shí)現(xiàn)該接口
        new InvocationHandler() { // 調(diào)用處理器,任何對(duì) B類所實(shí)現(xiàn)的接口方法的調(diào)用都會(huì)觸發(fā)此處理器
            @Override
            public Object invoke (Object proxy, // 這個(gè)是代理的實(shí)例,method.invoke時(shí)不能使用這個(gè),否則會(huì)死循環(huán)
                                  Method method, // 觸發(fā)的接口方法
                                  Object[] args // 此次調(diào)用該方法的參數(shù)
                                  ) throws Throwable {
                System.out.println(String.format("調(diào)用 %s 之前", method.getName()));
                /**
                 * 這里必須使用B類的某個(gè)具體實(shí)現(xiàn)類的實(shí)例,因?yàn)橛|發(fā)時(shí)這里的method只是一個(gè)接口方法的引用,
                 * 也就是說(shuō)它是空的,你需要為它指定具有邏輯的上下文(bInstance)。
                 */
                Object obj = method.invoke(bInstance, args);
                System.out.println(String.format("調(diào)用 %s 之后", method.getName()));
                return obj; //返回調(diào)用結(jié)果
            }
        }
    );
}

這樣你就可以攔截這個(gè)Java類的某個(gè)方法調(diào)用,但是你只能攔截到func1的調(diào)用,想想為什么?

那么注意了:

ClassLoader這是個(gè)class就會(huì)有,注解也不例外。那么注解和interfaces有什么關(guān)系?

注解本質(zhì)上就是一個(gè)接口,它的實(shí)質(zhì)定義為:interface SomeAnnotation extends Annotation。這個(gè)Annotation接口位于java/lang/annotation包,它的注釋中第一句話就是The common interface extended by all annotation types.

如此說(shuō)來(lái),F(xiàn)oo注解本身只是個(gè)接口,這就意味著它沒(méi)有任何代碼邏輯,那么它的value屬性究竟是存在哪里的呢?

展開(kāi)foo可以發(fā)現(xiàn):

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

這個(gè)Proxy實(shí)例持有一個(gè)AnnotationInvocationHandler,還記得之前提到過(guò)如何創(chuàng)建一個(gè)Proxy實(shí)例么? 第三個(gè)參數(shù)就是一個(gè)InvocationHandler。

看名字這個(gè)handler即是Annotation所特有的,我們看一下它的代碼:

?
1
2
3
4
5
6
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    private transient volatile Method[] memberMethods = null;    
    /* 后續(xù)無(wú)關(guān)代碼就省略了,想看的話可以查看 sun/reflect/annotation/AnnotationInvocationHandler */   
}

我們一眼就可以看到一個(gè)有意思的名字:memberValues,這是一個(gè)Map,而斷點(diǎn)中可以看到這是一個(gè)LinknedHashMap,key為注解的屬性名稱,value即為注解的屬性值。

現(xiàn)在我們找到了注解的屬性值存在哪里了,那么接下來(lái)的事就好辦了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Created by krun on 2017/9/18.
 */
public class Main {
    public static void main(String ...args) throws NoSuchFieldException, IllegalAccessException {
        //獲取Bar實(shí)例
        Bar bar = new Bar();
        //獲取Bar的val字段
        Field field = Bar.class.getDeclaredField("val");
        //獲取val字段上的Foo注解實(shí)例
        Foo foo = field.getAnnotation(Foo.class);
        //獲取 foo 這個(gè)代理實(shí)例所持有的 InvocationHandler
        InvocationHandler h = Proxy.getInvocationHandler(foo);
        // 獲取 AnnotationInvocationHandler 的 memberValues 字段
        Field hField = h.getClass().getDeclaredField("memberValues");
        // 因?yàn)檫@個(gè)字段事 private final 修飾,所以要打開(kāi)權(quán)限
        hField.setAccessible(true);
        // 獲取 memberValues
        Map memberValues = (Map) hField.get(h);
        // 修改 value 屬性值
        memberValues.put("value", "ddd");
        // 獲取 foo 的 value 屬性值
        String value = foo.value();
        System.out.println(value); // ddd
    }
}

通過(guò)反射動(dòng)態(tài)修改自定義注解屬性值

java/lang/reflect 這個(gè)包下面都是Java的反射類和工具。

Annotation 注解,也是位于這個(gè)包里的。

注解自從Java 5.0版本引入后,就成為了Java平臺(tái)中非常重要的一部分,常見(jiàn)的有 @Override、 @Deprecated

關(guān)于注解更詳細(xì)的信息和使用方法,網(wǎng)上已經(jīng)有很多資料,自行查看。

一個(gè)注解通過(guò) @Retention 指定其生命周期,本文所討論的動(dòng)態(tài)修改注解屬性值,建立在 @Retention(RetentionPolicy.RUNTIM) 這種情況。

這種注解才能在運(yùn)行時(shí)(runtime)通過(guò)反射機(jī)制進(jìn)行修改屬性的操作。

我們先定義一個(gè)自定義注解 @TestAnno

它有一個(gè)類型為 String 的 name屬性,該注解應(yīng)用再M(fèi)ethod上:

?
1
2
3
4
5
6
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface TestAnno {
   String name() default "";
}

我用自定義注解首先得了解清楚注解的值存儲(chǔ)在什么地方,我們可以寫(xiě)個(gè)main方法測(cè)試一下:

通過(guò)反射獲取注解@TestAnno的值

我們定義了一個(gè)RetryTestService 在它的方法 retryTest() 上添加@TestAnno 注解,然后在main方法里面反射獲取注解的name值

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class RetryTestService {
    @TimeLog
    @TestAnno(name = "${nba.kobe}")
    public String retryTest(){
        System.out.println("---進(jìn)行了接口請(qǐng)求....");
        return "success";
    }
    public static void main(String[] args) throws NoSuchMethodException {
        RetryTestService service = new RetryTestService();
        Method method = service.getClass().getDeclaredMethod("retryTest",null);
        TestAnno testAnno = method.getDeclaredAnnotation(TestAnno.class);
        System.out.println(testAnno.name());
    }
}

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

當(dāng)前棧中有這么幾個(gè)變量,不過(guò)其中有一點(diǎn)很特別:@TestAnno,其實(shí)是個(gè)Proxy實(shí)例。

Proxy也是 java/lang/reflect下的東西,它的作用是為一個(gè)Java類生成一個(gè)代理,就像這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface A {
    String func1();
}
public class B implements A {
    
    @Override
    public String func1() { //do something ... }   
    public String func2() { //do something ... };
}
public static void main(String ...args) {
    B bInstance = new B();   
    B bProxy = Proxy.newProxyInstance(
        B.class.getClassLoader(),    // B 類的類加載器
        B.class.getInterfaces(), // B 類所實(shí)現(xiàn)的接口,如果你想攔截B類的某個(gè)方法,必須讓這個(gè)方法在某個(gè)接口中聲明并讓B類實(shí)現(xiàn)該接口
        new InvocationHandler() { // 調(diào)用處理器,任何對(duì) B類所實(shí)現(xiàn)的接口方法的調(diào)用都會(huì)觸發(fā)此處理器
            @Override
            public Object invoke (Object proxy, // 這個(gè)是代理的實(shí)例,method.invoke時(shí)不能使用這個(gè),否則會(huì)死循環(huán)
                                  Method method, // 觸發(fā)的接口方法
                                  Object[] args // 此次調(diào)用該方法的參數(shù)
                                  ) throws Throwable {
                System.out.println(String.format("調(diào)用 %s 之前", method.getName()));
                /**
                 * 這里必須使用B類的某個(gè)具體實(shí)現(xiàn)類的實(shí)例,因?yàn)橛|發(fā)時(shí)這里的method只是一個(gè)接口方法的引用,
                 * 也就是說(shuō)它是空的,你需要為它指定具有邏輯的上下文(bInstance)。
                 */
                Object obj = method.invoke(bInstance, args);
                System.out.println(String.format("調(diào)用 %s 之后", method.getName()));
                return obj; //返回調(diào)用結(jié)果
            }
        }
    );
}

注意了:

ClassLoader 這是個(gè)class就會(huì)有,注解也不例外。那么注解和interfaces有什么關(guān)系?

注解本質(zhì)上就是一個(gè)接口,它的實(shí)質(zhì)定義為: interface SomeAnnotation extends Annotation。

這個(gè) Annotation 接口位于 java/lang/annotation 包,它的注釋中第一句話就是 The common interface extended by all annotation types.

如此說(shuō)來(lái),@TestAnno 注解本身只是個(gè)接口,這就意味著它沒(méi)有任何代碼邏輯,那么它的 value 屬性究竟是存在哪里的呢?

展開(kāi) @TestAnno 可以發(fā)現(xiàn):

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

這個(gè) Proxy 實(shí)例持有一個(gè) AnnotationInvocationHandler,還記得之前提到過(guò)如何創(chuàng)建一個(gè) Proxy 實(shí)例么? 第三個(gè)參數(shù)就是一個(gè) InvocationHandler。

看名字這個(gè)handler即是Annotation所特有的,我們看一下它的代碼:

?
1
2
3
4
5
6
7
8
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    private transient volatile Method[] memberMethods = null;
    
    /* 后續(xù)無(wú)關(guān)代碼就省略了,想看的話可以查看 sun/reflect/annotation/AnnotationInvocationHandler */
   
}

我們一眼就可以看到一個(gè)有意思的名字: memberValues,這是一個(gè)Map,而斷點(diǎn)中可以看到這是一個(gè) LinknedHashMap,key為注解的屬性名稱,value即為注解的屬性值。

現(xiàn)在我們找到了注解的屬性值存在哪里了,那么接下來(lái)的事就好辦了:

我這里寫(xiě)兩個(gè)aop。第一個(gè)aop攔截帶@TestAnno注解的方法,然后改變注解的name值,第二個(gè)aop我們?cè)侔炎⒔獾膎ame值打印出來(lái),看看是不是真被改了

第一個(gè)aop:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Aspect
@Component
@Order(1) //aop執(zhí)行順序1表示先執(zhí)行此aop
public class AuthDemoAspect implements EnvironmentAware {
    Environment environment;
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
 
    @Pointcut("@annotation(com.ali.hangz.tooltest.config.TestAnno)")
    public void myPointCut() {
    }
 
    @Before(value = "myPointCut()")
    public void check(){
    }
 
    @After(value = "myPointCut()")
    public void bye(){
    }
  
    /**
     *配置文件配置
     * @return
     */
    @Around("myPointCut() && @annotation(testAnno)")
    public Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
        try {
            System.out.println("---修改前注解@TestAnno的name指為:" + testAnno.name());
            String s = environment.resolvePlaceholders(testAnno.name());
            //獲取 foo 這個(gè)代理實(shí)例所持有的 InvocationHandler
            InvocationHandler h = Proxy.getInvocationHandler(testAnno);
            // 獲取 AnnotationInvocationHandler 的 memberValues 字段
            Field hField = h.getClass().getDeclaredField("memberValues");
            // 因?yàn)檫@個(gè)字段事 private final 修飾,所以要打開(kāi)權(quán)限
            hField.setAccessible(true);
            // 獲取 memberValues
            Map memberValues = (Map) hField.get(h);
            // 修改 value 屬性值
            memberValues.put("name",s);
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }
}

第一個(gè)aop里面我改變注解的name值,由上面service方法上注解的${nba.kobe} 改成讀取配置文件 nba.kobe的配置值

項(xiàng)目配置文件:application.properties增加一個(gè)值

?
1
2
nba.kobe=科比
String s = environment.resolvePlaceholders(testAnno.name());

這行代碼其實(shí)就是通過(guò)原本注解值${nba.kobe}去配置文件取nba.kobe 對(duì)應(yīng)的值。如果你只是修改原來(lái)注解的name值而不是去取配置文件大可以不用此行代碼,直接給memberValues 里面的name put新的值就行。

注意:@Order(1) 可以控制aop的執(zhí)行順序

然后我再寫(xiě)第二個(gè)aop,打印出注解@TestAnno 的name值看看是不是第一個(gè)aop已經(jīng)成功把值改掉了

第二個(gè)aop:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Aspect
@Component
@Order(2)
public class AuthDemoAspectTwo implements EnvironmentAware { 
    Environment environment; 
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
 
    @Pointcut("@annotation(com.ali.hangz.tooltest.config.TestAnno)")
    public void myPointCut() {
    }
 
    @Before(value = "myPointCut()")
    public void check(){
    }
 
    @After(value = "myPointCut()")
    public void bye(){
    }
 
    /**
     *配置文件配置
     * @return
     */
    @Around("myPointCut() && @annotation(testAnno)")
    public Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
        try {
            System.out.println("---修改后的注解名稱:" + testAnno.name());
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

然后我們只需要啟動(dòng)項(xiàng)目調(diào)用一下RetryTestService的 retryTest()方法 就可以進(jìn)入aop 看看打印出來(lái)的結(jié)果了

Java通過(guò)反射,如何動(dòng)態(tài)修改注解的某個(gè)屬性值

通過(guò)結(jié)果我們可以發(fā)現(xiàn)第一個(gè)aop的確把retryTest()方法上面注解@TestAnno的name值由原先的 @TestAnno(name = "${nba.kobe}") ${nba.kobe}值動(dòng)態(tài)修改成了配置文件里面配置的“科比”了。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://segmentfault.com/a/1190000011213222

延伸 · 閱讀

精彩推薦
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對(duì)java開(kāi)發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程升級(jí)IDEA后Lombok不能使用的解決方法

    升級(jí)IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級(jí),尋思已經(jīng)有好久沒(méi)有升過(guò)級(jí)了。升級(jí)完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來(lái)介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對(duì)于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7472021-02-04
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對(duì)象的轉(zhuǎn)換詳解

    xml與Java對(duì)象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對(duì)象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
主站蜘蛛池模板: 精品一区二区三区中文 | 亚洲AV无码国产精品色午夜情 | 美女和男人一起差差 | 国产一级持黄大片99久久 | 午夜大片在线观看 | 欧美灰丝袜丝交nylons | 亚洲欧美精品一区天堂久久 | 国产精品吹潮香蕉在线观看 | 视频亚洲一区 | 日本在线色 | 国产播放器一区 | 好大用力深一点女公交车 | 欧美高清国产 | 韩国久播影院理论片不卡影院 | 国产拍拍视频一二三四区 | 无码任你躁久久久久久久 | 狠狠鲁视频| www视频免费观看 | 91制片厂果冻星空传媒3xg | 99自拍视频在线观看 | 毛片区| 欧亚尺码专线欧洲s码wmy | 国产精品99久久 | 秋霞黄色网| 我和岳偷长篇小说 | 国产一级大片免费看 | 5g影院天天5g天天爽大陆 | 精品福利视频一区二区三区 | 第一福利在线观看永久视频 | 亚洲精品二三区伊人久久 | 女主被当众调教虐np | 亚洲一区二区三区不卡在线播放 | 精品一产品大全 | 国产乱人乱精一区二区视频密 | 日本大巴车强thepro | 美女的隐私无遮挡撒尿 | 国产日韩一区二区三区在线播放 | 99国内精品久久久久久久黑人 | 风间由美理论片在线观看 | 成人精品 | 国产99页|