29--Pointcut和Advisor以及靜態(tài)普通方法名匹配切面

上一篇我們簡(jiǎn)單介紹了一下AOP中的一些相關(guān)術(shù)語盖呼、以及Advice接口下的一些增強(qiáng)實(shí)現(xiàn),但是這里會(huì)有一個(gè)問題化撕,那就是增強(qiáng)方法還會(huì)被應(yīng)用到目標(biāo)類的所有接口几晤。修改一下上一節(jié)的測(cè)試類并運(yùn)行。(本篇很多簡(jiǎn)介摘自Spring3.X企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)植阴,實(shí)在想不出來如何去介紹這些概念類的信息蟹瘾。。墙贱。)

1.Pointcut概念的引入及簡(jiǎn)介
@Test
public void test5() {
    // 前置增強(qiáng)
    // 1热芹、實(shí)例化bean和增強(qiáng)
    Animal dog = new Dog();
    MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice();

    // 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強(qiáng)
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(dog);
    proxyFactory.addAdvice(advice);

    // 3惨撇、生成代理實(shí)例
    Animal proxyDog = (Animal) proxyFactory.getProxy();
    proxyDog.sayHello("二哈", 3);
    System.out.println("\n\n");
    proxyDog.sayException("二哈", 3);
}
==前置增強(qiáng)
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@65e579dc
==名字:二哈 年齡:3



==前置增強(qiáng)
==方法名:sayException
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@65e579dc
==名字:二哈 年齡:3

java.lang.ArithmeticException: / by zero

    at com.lyc.cn.v2.day04.advisor.Dog.sayException(Dog.java:17)
    at com.lyc.cn.v2.day04.advisor.Dog$$FastClassBySpringCGLIB$$a974b1ec.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)

從測(cè)試結(jié)果上看,只要我們調(diào)用了Animal類的接口府寒,增強(qiáng)方法都會(huì)被應(yīng)用到目標(biāo)類的方法上魁衙,這樣的增強(qiáng)效果肯定不能滿足我們實(shí)際的應(yīng)用,那么這個(gè)時(shí)候就需要引入一個(gè)概念----切入點(diǎn)(Pointcut)株搔。通過切入點(diǎn)就可以有選擇的將增強(qiáng)應(yīng)用到目標(biāo)類的方法上剖淀,而目標(biāo)類的方法就是我們上一節(jié)說的連接點(diǎn),即sayHello和sayException方法纤房,目標(biāo)類就是要被增強(qiáng)的類纵隔,即Dog類,所以增強(qiáng)描述了連接點(diǎn)的方位信息,例如織入到方法之前捌刮、方法之后碰煌,而切入點(diǎn)則進(jìn)一步的描述了織入到那些類的那些方法上。到這里相信大家對(duì)連接點(diǎn)绅作、切入點(diǎn)芦圾、增強(qiáng)、目標(biāo)對(duì)象等概念有了更為深刻的理解俄认。

但是這又帶了一個(gè)新的問題,那就是如何將切入點(diǎn)定位到連接點(diǎn),換言之狂塘,就是切入點(diǎn)如何知道自己要被應(yīng)用到那些連接點(diǎn)上呢懒震?

接下來就有必要看一下Pointcut接口的源碼了。

  • Pointcut接口
public interface Pointcut {
    /**
     * 返回當(dāng)前切點(diǎn)匹配的類
     */
    ClassFilter getClassFilter();

    /**
     * 返回當(dāng)前切點(diǎn)匹配的方法
     */
    MethodMatcher getMethodMatcher();


    /**
     * Canonical Pointcut instance that always matches.
     */
    Pointcut TRUE = TruePointcut.INSTANCE;

}

Pointcut接口的定義非常簡(jiǎn)單岂贩,僅僅包含了ClassFilter和MethodMatcher的定義糊探,ClassFilter可以定位到具體的類上,MethodMatcher可以定位到具體的方法上河闰,這樣通過Pointcut我們就可以將將增強(qiáng)織入到特定類的特定方法上了科平。再來看下ClassFilter和MethodMatcher的定義:

  • ClassFilter接口
public interface ClassFilter {

    /**
     * 切入點(diǎn)應(yīng)該應(yīng)用于給定的接口還是目標(biāo)類
     * Should the pointcut apply to the given interface or target class?
     * @param clazz the candidate target class 候選目標(biāo)類
     * @return whether the advice should apply to the given target class 增強(qiáng)是否應(yīng)用于目標(biāo)類
     */
    boolean matches(Class<?> clazz);


    /**
     * Canonical instance of a ClassFilter that matches all classes.
     */
    ClassFilter TRUE = TrueClassFilter.INSTANCE;

}
  • MethodMatcher接口
public interface MethodMatcher {

    /**
     * 靜態(tài)方法匹配判斷
     */
    boolean matches(Method method, Class<?> targetClass);

    /**
     * 判斷靜態(tài)方法匹配或動(dòng)態(tài)方法匹配
     * true:動(dòng)態(tài)方法匹配
     * false:靜態(tài)方法匹配
     */
    boolean isRuntime();

    /**
     * 動(dòng)態(tài)方法匹配判斷
     */
    boolean matches(Method method, Class<?> targetClass, Object... args);


    /**
     * Canonical instance that matches all methods.
     */
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

雖然還沒有看到以上三個(gè)接口的具體實(shí)現(xiàn),但是現(xiàn)在我們只要知道Pointcut接口提供了這樣的功能就行了姜性。在MethodMatcher接口中又引入了一個(gè)新的概念瞪慧,方法匹配模式,Spring支持兩種方法匹配器:

  • 靜態(tài)方法匹模式:所謂靜態(tài)方法匹配器部念,僅對(duì)方法名簽名(包括方法名和入?yún)㈩愋图绊樞颍┻M(jìn)行匹配弃酌。
  • 動(dòng)態(tài)方法匹配器:動(dòng)態(tài)方法匹配器會(huì)在運(yùn)行期方法檢查入?yún)⒌闹怠?靜態(tài)匹配僅會(huì)判斷一次,而動(dòng)態(tài)匹配因?yàn)槊看握{(diào)用方法的入?yún)⒖赡懿灰粯永芰叮悦看握{(diào)用方法都必須判斷妓湘。

接下來簡(jiǎn)單介紹一下Spring提供的切點(diǎn)類型:

  • 靜態(tài)方法切點(diǎn)-->org.springframework.aop.support.StaticMethodMatcherPointcut
    靜態(tài)方法切點(diǎn)的抽象基類,默認(rèn)情況下匹配所有的類乌询。最常用的兩個(gè)子類NameMatchMethodPointcut和 AbstractRegexpMethodPointcut 榜贴, 前者提供簡(jiǎn)單字符串匹配方法簽名,后者使用正則表達(dá)式匹配方法簽名妹田。
  • 動(dòng)態(tài)方法切點(diǎn)-->org.springframework.aop.support.DynamicMethodMatcherPointcut
    動(dòng)態(tài)方法切點(diǎn)的抽象基類唬党,默認(rèn)情況下匹配所有的類
  • 注解切點(diǎn)-->org.springframework.aop.support.annotation.AnnotationMatchingPointcut
  • 表達(dá)式切點(diǎn)-->org.springframework.aop.support.ExpressionPointcut
    提供了對(duì)AspectJ切點(diǎn)表達(dá)式語法的支持
  • 流程切點(diǎn)-->org.springframework.aop.support.ControlFlowPointcut
    該切點(diǎn)是一個(gè)比較特殊的節(jié)點(diǎn),它根據(jù)程序執(zhí)行的堆棧信息查看目標(biāo)方法是否由某一個(gè)方法直接或間接發(fā)起調(diào)用鬼佣,一次來判斷是否為匹配的鏈接點(diǎn)
  • 復(fù)合切點(diǎn)-->org.springframework.aop.support.ComposablePointcut
    該類是為實(shí)現(xiàn)創(chuàng)建多個(gè)切點(diǎn)而提供的操作類
2.切面簡(jiǎn)介

由于增強(qiáng)包括橫切代碼驶拱,又包含部分連接點(diǎn)信息(方法前、方法后主方位信息)晶衷,所以可以僅通過增強(qiáng)類生成一個(gè)切面蓝纲。 但切點(diǎn)僅僅代表目標(biāo)類連接點(diǎn)的部分信息(類和方法的定位)阴孟,所以僅有切點(diǎn)無法制作出一個(gè)切面,必須結(jié)合增強(qiáng)才能制作出切面税迷。Spring使用org.springframework.aop.Advisor接口標(biāo)識(shí)切面概念永丝,一個(gè)切面同時(shí)包含橫切代碼和連接點(diǎn)信息。

切面可以分為3類:一般切面翁狐、切點(diǎn)切面类溢、引介切面

  • 一般切面Advisor
    org.springframework.aop.Advisor代表一般切面,僅包含一個(gè)Advice ,因?yàn)锳dvice包含了橫切代碼和連接點(diǎn)信息露懒,所以Advice本身一個(gè)簡(jiǎn)單的切面闯冷,只不過它代表的橫切的連接點(diǎn)是所有目標(biāo)類的所有方法,因?yàn)檫@個(gè)橫切面太寬泛懈词,所以一般不會(huì)直接使用蛇耀。

  • 切點(diǎn)切面PointcutAdvisor
    org.springframework.aop.PointcutAdvisor ,代表具有切點(diǎn)的切面,包括Advice和Pointcut兩個(gè)類坎弯,這樣就可以通過類纺涤、方法名以及方位等信息靈活的定義切面的連接點(diǎn),提供更具實(shí)用性的切面抠忘。PointcutAdvisor主要有6個(gè)具體的實(shí)現(xiàn)類:

    1. DefaultPointcutAdvisor:最常用的切面類型撩炊,它可以通過任意Pointcut和Advice定義一個(gè)切面,唯一不支持的就是引介的切面類型崎脉,一般可以通過擴(kuò)展該類實(shí)現(xiàn)自定義的切面
    2. NameMatchMethodPointcutAdvisor:通過該類可以定義按方法名定義切點(diǎn)的切面
    3. AspectJExpressionPointcutAdvisor:用于AspectJ切點(diǎn)表達(dá)式定義切點(diǎn)的切面
    4. StaticMethodMatcherPointcutAdvisor:靜態(tài)方法匹配器切點(diǎn)定義的切面拧咳,默認(rèn)情況下匹配所有的的目標(biāo)類
    5. AspectJPointcutAdvisor:用于AspectJ語法定義切點(diǎn)的切面
  • 引介切面IntroductionAdvisor
    org.springframework.aop.IntroductionAdvisor代表引介切面, 引介切面是對(duì)應(yīng)引介增強(qiáng)的特殊的切面囚灼,它應(yīng)用于類層上面骆膝,所以引介切點(diǎn)使用ClassFilter進(jìn)行定義。
3.靜態(tài)普通方法名匹配切面

上面已經(jīng)對(duì)切入點(diǎn)灶体、切面做了簡(jiǎn)介阅签,下面通過幾個(gè)例子來加深大家的印象。先來看靜態(tài)普通方法名匹配切面蝎抽,前面我們介紹切入點(diǎn) 通過ClassFilter可以定位到具體的類上政钟,MethodMatcher可以定位到具體的方法上,那么接下來通過定義一個(gè)接口、兩個(gè)類织中。并通過實(shí)現(xiàn)類中的不同方法來驗(yàn)證我們之前的介紹锥涕。

  • 接口和實(shí)現(xiàn)類(目標(biāo)對(duì)象)
package com.lyc.cn.v2.day05;

/**
 * @author: LiYanChao
 * @create: 2018-11-04 22:40
 */
public interface Animal {
    void sayHello();
}
package com.lyc.cn.v2.day05;

/**
 * @author: LiYanChao
 * @create: 2018-11-04 22:09
 */
public class Cat implements Animal {

    @Override
    public void sayHello() {
        System.out.println("我是Cat類的sayHello方法。狭吼。。");
    }

    public void sayHelloCat() {
        System.out.println("我是一只貓殖妇。刁笙。。");
    }

}
package com.lyc.cn.v2.day05;

/**
 * @author: LiYanChao
 * @create: 2018-11-04 22:09
 */
public class Dog implements Animal{

    @Override
    public void sayHello() {
        System.out.println("我是Dog類的sayHello方法。疲吸。座每。");
    }

    public void sayHelloDog() {
        System.out.println("我是一只狗。摘悴。峭梳。");
    }
}
  • 增強(qiáng)(為了演示,這里只實(shí)現(xiàn)MethodBeforeAdvice前置增強(qiáng)接口)
package com.lyc.cn.v2.day05;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * 前置增強(qiáng)
 * @author: LiYanChao
 * @create: 2018-11-01 21:50
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==前置增強(qiáng)");
        System.out.println("==方法名:" + method.getName());
        if (null != args && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
            }
        }
        System.out.println("==目標(biāo)類信息:" + target.toString());
    }

}
  • 切面
package com.lyc.cn.v2.day05;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;

import java.lang.reflect.Method;

/**
 * 靜態(tài)普通方法名匹配切面
 * @author: LiYanChao
 * @create: 2018-11-04 22:08
 */
public class MyStaticPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {

    private static String METHOD_NAME = "sayHello";

    /**
     * 靜態(tài)方法匹配判斷蹂喻,這里只有方法名為sayHello的葱椭,才能被匹配
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return METHOD_NAME.equals(method.getName());
    }

    /**
     * 覆蓋getClassFilter,只匹配Dog類
     * @return
     */
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                return Dog.class.isAssignableFrom(clazz);
            }
        };
    }
}

StaticMethodMatcherPointcutAdvisor抽象類繼承了StaticMethodMatcherPointcut類并實(shí)現(xiàn)了PointcutAdvisor接口口四。在MyStaticPointcutAdvisor類中我們實(shí)現(xiàn)了matches靜態(tài)方法匹配判斷孵运,并且只有方法名為sayHello的,才能被匹配蔓彩;覆蓋了getClassFilter方法治笨,并且只匹配Dog類。

  • 測(cè)試一
    為了大家能夠更便捷的使用測(cè)試類赤嚼,也為了減少大家書寫配置文件的負(fù)擔(dān)旷赖,我們還是采用編碼的形式實(shí)現(xiàn)。新建MyTest類并書寫測(cè)試方法更卒。
@Test
public void test1() {
    // 1等孵、創(chuàng)建目標(biāo)類、增強(qiáng)逞壁、切入點(diǎn)
    Animal animal = new Dog();
    MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice();
    MyStaticPointcutAdvisor advisor = new MyStaticPointcutAdvisor();

    // 2流济、創(chuàng)建ProxyFactory并設(shè)置目標(biāo)類、增強(qiáng)腌闯、切面
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(animal);
    // 為切面類提供增強(qiáng)
    advisor.setAdvice(advice);
    proxyFactory.addAdvisor(advisor);

    // 3绳瘟、生成代理實(shí)例
    Dog proxyDog = (Dog) proxyFactory.getProxy();
    proxyDog.sayHelloDog();
    System.out.println("\n\n");
    proxyDog.sayHello();

}
我是一只狗。姿骏。糖声。



==前置增強(qiáng)
==方法名:sayHello
==目標(biāo)類信息:com.lyc.cn.v2.day05.Dog@65e579dc
我是Dog類的sayHello方法。分瘦。蘸泻。

之前我們?cè)诖a里配置了,在類一級(jí)只匹配Dog類嘲玫,在方法一級(jí)只匹配sayHello方法悦施。運(yùn)行結(jié)果與我們的設(shè)置符合。只有Dog類的sayHello被增強(qiáng)了去团。

  • 測(cè)試二
@Test
public void test2() {
    // 1抡诞、創(chuàng)建目標(biāo)類穷蛹、增強(qiáng)、切入點(diǎn)
    Animal animal = new Cat();
    MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice();
    MyStaticPointcutAdvisor advisor = new MyStaticPointcutAdvisor();

    // 2昼汗、創(chuàng)建ProxyFactory并設(shè)置目標(biāo)類肴熏、增強(qiáng)、切面
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(animal);
    // 為切面類提供增強(qiáng)
    advisor.setAdvice(advice);
    proxyFactory.addAdvisor(advisor);

    // 3顷窒、生成代理實(shí)例
    Cat proxyDog = (Cat) proxyFactory.getProxy();
    proxyDog.sayHelloCat();
    System.out.println("\n\n");
    proxyDog.sayHello();

}
我是一只貓蛙吏。。鞋吉。



我是Cat類的sayHello方法鸦做。。坯辩。

測(cè)試二的結(jié)果沒有一個(gè)方法被增強(qiáng)馁龟,雖然在Cat類中也有sayHello方法,但是我們?cè)O(shè)置的是只匹配Dog類漆魔,所以雖然在Cat類中有sayHello方法坷檩,但是它也是無法被增強(qiáng)的。

至于其他的切點(diǎn)和切面改抡,這里就不一一演示了矢炼,這里特別感謝《Spring3.X企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)》這本書,本篇很多介紹均摘自本書阿纤,哈哈句灌!

4.總結(jié)

本篇主要介紹了切點(diǎn)和切面的概念,并通過實(shí)際的例子為大家演示了切點(diǎn)是如何匹配類和方法的欠拾。概念性的東西大家只看簡(jiǎn)介是不行的胰锌,需要自己動(dòng)手寫代碼,才能更深刻的理解AOP的相關(guān)概念藐窄,在接下來的源碼分析中才不會(huì)陷入迷茫资昧。

上一篇和本篇的測(cè)試類中,我們都是通過ProxyFactory創(chuàng)建的代理荆忍,這樣的實(shí)現(xiàn)肯定無法滿足我們的實(shí)際需要格带,那么接下來的篇幅,我們就要介紹Spring的自動(dòng)代理機(jī)制刹枉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末叽唱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子微宝,更是在濱河造成了極大的恐慌棺亭,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟋软,死亡現(xiàn)場(chǎng)離奇詭異侦铜,居然都是意外死亡专甩,警方通過查閱死者的電腦和手機(jī)钟鸵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門钉稍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棺耍,你說我怎么就攤上這事贡未。” “怎么了蒙袍?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵俊卤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我害幅,道長(zhǎng)消恍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任以现,我火速辦了婚禮狠怨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邑遏。我一直安慰自己佣赖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布记盒。 她就那樣靜靜地躺著憎蛤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纪吮。 梳的紋絲不亂的頭發(fā)上俩檬,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音碾盟,去河邊找鬼棚辽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛巷疼,可吹牛的內(nèi)容都是我干的晚胡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嚼沿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼估盘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骡尽,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤遣妥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后攀细,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箫踩,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爱态,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了境钟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锦担。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慨削,靈堂內(nèi)的尸體忽然破棺而出洞渔,到底是詐尸還是另有隱情,我是刑警寧澤缚态,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布磁椒,位于F島的核電站,受9級(jí)特大地震影響玫芦,放射性物質(zhì)發(fā)生泄漏浆熔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一桥帆、第九天 我趴在偏房一處隱蔽的房頂上張望医增。 院中可真熱鬧,春花似錦环葵、人聲如沸调窍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邓萨。三九已至,卻和暖如春菊卷,著一層夾襖步出監(jiān)牢的瞬間缔恳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工洁闰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歉甚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓扑眉,卻偏偏與公主長(zhǎng)得像纸泄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腰素,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容