27--靜態(tài)代理模式和JDK、CGLIB動(dòng)態(tài)代理

前面的章節(jié)竿开,已經(jīng)分析了IoC容器的源碼谱仪,接下來(lái)的章節(jié)來(lái)分析Spring的另一個(gè)核心功能AOP。為了更好的分析源碼德迹,需要先溫習(xí)一下動(dòng)態(tài)代理的知識(shí)芽卿,如果對(duì)java的動(dòng)態(tài)代理無(wú)所了解的話(huà)揭芍,那么對(duì)AOP源碼的分析就無(wú)從談起胳搞。代理模式可分為靜態(tài)代理和動(dòng)態(tài)代理兩種。而動(dòng)態(tài)代理又有JDK、CGLIB動(dòng)態(tài)代理肌毅。下面我們逐步分析這幾種代理筷转。

1.靜態(tài)代理

  • 被代理接口和實(shí)現(xiàn)類(lèi)
package com.lyc.cn.v2.day04.proxy.proxy;

/**
 * 賬戶(hù)接口
 */
public interface Count {
    // 查詢(xún)賬戶(hù)
    void queryCount();

    // 修改賬戶(hù)
    void updateCount();
}
package com.lyc.cn.v2.day04.proxy.proxy;

/**
 * @author: LiYanChao
 * @create: 2018-10-21 12:07
 */
public class CountImpl implements Count {
    @Override
    public void queryCount() {
        System.out.println("==查詢(xún)賬戶(hù)");
    }

    @Override
    public void updateCount() {
        System.out.println("==更新賬戶(hù)");
    }
}

接口只模擬了兩個(gè)接口,賬戶(hù)查詢(xún)和賬戶(hù)更新悬而,并在實(shí)現(xiàn)類(lèi)里進(jìn)行了打印呜舒。

  • 代理類(lèi)
package com.lyc.cn.v2.day04.proxy.proxy;

/**
 * 代理類(lèi)
 * @author: LiYanChao
 * @create: 2018-10-21 12:08
 */
public class CountProxy implements Count {

    private CountImpl countImpl;

    /**
     * 覆蓋默認(rèn)構(gòu)造器
     */
    public CountProxy(CountImpl countImpl) {
        this.countImpl = countImpl;
    }

    @Override
    public void queryCount() {
        System.out.println("==查詢(xún)賬戶(hù)開(kāi)始");
        // 調(diào)用真正的查詢(xún)賬戶(hù)方法
        countImpl.queryCount();
        System.out.println("==查詢(xún)賬戶(hù)結(jié)束");
    }

    @Override
    public void updateCount() {
        System.out.println("==更新賬戶(hù)開(kāi)始");
        // 調(diào)用真正的修改賬戶(hù)操作
        countImpl.updateCount();
        System.out.println("==更新賬戶(hù)結(jié)束");
    }
}
  • 測(cè)試及結(jié)果
@Test
public void test1() {
    // 靜態(tài)代理
    CountImpl countImpl = new CountImpl();
    CountProxy countProxy = new CountProxy(countImpl);
    countProxy.updateCount();
    System.out.println("\n*******\n");
    countProxy.queryCount();
}
==更新賬戶(hù)開(kāi)始
==更新賬戶(hù)
==更新賬戶(hù)結(jié)束

*******

==查詢(xún)賬戶(hù)開(kāi)始
==查詢(xún)賬戶(hù)
==查詢(xún)賬戶(hù)結(jié)束

該中模式比較簡(jiǎn)單,不再做過(guò)多的分析笨奠。

2.JDK動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理所用到的代理類(lèi)在程序調(diào)用到代理類(lèi)對(duì)象時(shí)才由JVM真正創(chuàng)建袭蝗,JVM根據(jù)傳進(jìn)來(lái)的業(yè)務(wù)實(shí)現(xiàn)類(lèi)對(duì)象以及方法名 ,動(dòng)態(tài)地創(chuàng)建了一個(gè)代理類(lèi)的class文件并被字節(jié)碼引擎執(zhí)行般婆,然后通過(guò)該代理類(lèi)對(duì)象進(jìn)行方法調(diào)用到腥。我們需要做的,只需指定代理類(lèi)的預(yù)處理蔚袍、調(diào)用后操作即可乡范。JDK的動(dòng)態(tài)代理需要實(shí)現(xiàn)InvocationHandler接口,并重寫(xiě)invoke方法啤咽。并且被代理的類(lèi)必須有接口晋辆,來(lái)看具體的例子:

  • 被代理接口和類(lèi)
package com.lyc.cn.v2.day04.proxy.jdk;

public interface JDKAnimal {
    void sayHello();
}
package com.lyc.cn.v2.day04.proxy.jdk;

public class JDKDog implements JDKAnimal {
    @Override
    public void sayHello() {
        System.out.println("我是一只貓");
    }
}
  • 自定義InvocationHandler
package com.lyc.cn.v2.day04.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {

    // 目標(biāo)對(duì)象
    private Object target;

    /**
     * 構(gòu)造方法
     * @param target 目標(biāo)對(duì)象
     */
    public MyInvocationHandler(Object target) {
        super();
        this.target = target;
    }

    /**
     * @param proxy  JDK動(dòng)態(tài)生成的最終代理對(duì)象
     * @param method 調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
     * @param args   調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("==代理方法開(kāi)始執(zhí)行");
        Object invoke = method.invoke(target, args);
        System.out.println("==代理方法結(jié)束執(zhí)行");
        return invoke;
    }

    /**
     * 獲取目標(biāo)對(duì)象的代理對(duì)象
     * @return 代理對(duì)象
     */
    public Object getProxy() {
        /**
         * 參數(shù):
         * 1、獲取當(dāng)前線程的類(lèi)加載
         * 2宇整、獲取接口
         * 3瓶佳、當(dāng)前對(duì)象
         */
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

}
  • 測(cè)試及結(jié)果
@Test
public void test2() {
    // JDK動(dòng)態(tài)代理
    MyInvocationHandler handler = new MyInvocationHandler(new JDKDog());
    JDKAnimal proxy = (JDKAnimal) handler.getProxy();
    proxy.sayHello();
}
==代理方法開(kāi)始執(zhí)行
我是一只貓
==代理方法結(jié)束執(zhí)行
3.JDK動(dòng)態(tài)代理原理分析

在MyInvocationHandler類(lèi)中,通過(guò)實(shí)現(xiàn)InvocationHandler并重寫(xiě)invoke方法没陡,實(shí)現(xiàn)了對(duì)JDKDog類(lèi)的動(dòng)態(tài)代理涩哟。但是這里大家一定會(huì)有一個(gè)疑問(wèn),在測(cè)試類(lèi)中通過(guò)JDKAnimal proxy = (JDKAnimal) handler.getProxy();獲取了代理類(lèi)的實(shí)例盼玄,但是當(dāng)調(diào)用proxy.sayHello();方法時(shí)贴彼,卻會(huì)調(diào)用MyInvocationHandler的invoke方法,要解開(kāi)這個(gè)謎題埃儿,就需要了解一下代理類(lèi)是如何生成器仗、生成的代理類(lèi)是什么樣子的、是如何被實(shí)例化的童番。

  • Proxy.newProxyInstance方法簡(jiǎn)析
    打開(kāi)MyInvocationHandler類(lèi)的getProxy方法精钮,查看Proxy.newProxyInstance方法源碼:
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 創(chuàng)建生成代理類(lèi)的實(shí)例
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

如果大家對(duì)這段代碼不感興趣或者覺(jué)得很枯燥的話(huà),沒(méi)關(guān)系剃斧,大家只要知道在這個(gè)方法里生成了代理類(lèi)的實(shí)例就行了轨香,但是生成的代理類(lèi)是什么樣子的呢?可以通過(guò)ProxyGenerator來(lái)幫助我們幼东。

  • 生成代理類(lèi)的.class文件
package com.lyc.cn.v2.day04.proxy.jdk;

import org.junit.Test;
import sun.misc.ProxyGenerator;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author: LiYanChao
 * @create: 2018-11-02 14:11
 */
public class GenJdkProxyClass {

    /**
     * 生成代理類(lèi)的名稱(chēng)
     */
    private static String DEFAULT_CLASS_NAME = "$Proxy";

    /**
     * 默認(rèn)生成的文件全路徑
     */
    private static String DEFAULT_FILE_PATH = "/Users/liyanchao/Desktop/" + DEFAULT_CLASS_NAME + ".class";


    /**
     * 使用ProxyGenerator生成代理類(lèi).class文件
     * @param path 文件路徑
     */
    public static void genProxyClass(String path) {

        byte[] classFile = ProxyGenerator.generateProxyClass(DEFAULT_CLASS_NAME, new Class[]{JDKAnimal.class});
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void TestGenProxyClass() {
        GenJdkProxyClass.genProxyClass(DEFAULT_FILE_PATH);
    }
}

可以根據(jù)自己的需要修改生成的類(lèi)名和路徑臂容。運(yùn)行測(cè)試方法生成.class文并進(jìn)行反編譯科雳,如果沒(méi)有反編譯軟件的話(huà),可以下載JD-GUI 可以實(shí)現(xiàn)對(duì).class文件的反編譯脓杉,該軟件支持windows糟秘、linux、macOS球散。用JD-GUI打開(kāi)生成的$Proxy.class文件尿赚,并將反編譯的類(lèi)導(dǎo)入IDEA(直接在IDEA里新建同名文件將代碼復(fù)制粘貼進(jìn)去即可)。反編譯后的源碼如下:

package com.lyc.cn.v2.day04.proxy.jdk;

/**
 * 反編譯代理.class文件
 * @author: LiYanChao
 * @create: 2018-11-02 15:01
 */

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy extends Proxy implements JDKAnimal {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        try {
            return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    /**
     * 代理類(lèi)中實(shí)現(xiàn)了sayHello接口
     */
    public final void sayHello() {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final String toString() {
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.lyc.cn.v2.day04.proxy.jdk.JDKAnimal").getMethod("sayHello", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            // return 會(huì)報(bào)錯(cuò)蕉堰,注釋掉即可凌净。
            //return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

代理類(lèi)$Proxy被聲明為final類(lèi),繼承Proxy類(lèi)并實(shí)現(xiàn)了JDKAnimal接口屋讶,重寫(xiě)了equals泻蚊、toString、hashCode等方法丑婿,當(dāng)然最重要的還是實(shí)現(xiàn)了JDKAnimal接口中的sayHello方法性雄,并通過(guò)靜態(tài)代碼塊拿到了sayHello方法的信息,看到sayHello方法體羹奉,看到這里相信大家對(duì)前面提到的疑問(wèn)已經(jīng)恍然大悟了秒旋。再通過(guò)一個(gè)測(cè)試方法加深大家的印象:

@Test
public void test3() {
    // JDK動(dòng)態(tài)代理測(cè)試反編譯后的生成代理類(lèi)
    MyInvocationHandler handler = new MyInvocationHandler(new JDKDog());
    $Proxy $proxy = new $Proxy(handler);
    $proxy.sayHello();
}

new $Proxy(handler)構(gòu)造函數(shù)接受的就是我們自定義的MyInvocationHandler,所以當(dāng)代碼運(yùn)行到$Proxy的sayHello方法時(shí)诀拭,this.h.invoke(this, m3, null); this.h就是MyInvocationHandler的實(shí)例迁筛,所以自然就會(huì)調(diào)用到invoke方法了,因?yàn)?code>JDKAnimal proxy = (JDKAnimal) handler.getProxy();獲取到的是代理類(lèi)的實(shí)例耕挨,而不是JDKAnimal的實(shí)例细卧。

4.CGLIB動(dòng)態(tài)代理

CGLIB是針對(duì)類(lèi)來(lái)實(shí)現(xiàn)代理的,原理是對(duì)指定的業(yè)務(wù)類(lèi)生成一個(gè)子類(lèi)筒占,并覆蓋其中業(yè)務(wù)方法實(shí)現(xiàn)代理贪庙。因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類(lèi)進(jìn)行代理翰苫。 在使用的時(shí)候需要引入cglib和asm的jar包止邮,并實(shí)現(xiàn)MethodInterceptor接口。

本例是在Spring源碼下新建的Gradle模塊奏窑,所以引入jar包的方式如下导披,如果是maven工程或者其他的工程的話(huà),可自行導(dǎo)入jar包埃唯。

compile group: 'asm', name: 'asm', version: '3.3.1'
compile group: 'cglib', name: 'cglib', version: '2.2.2'
  • 被代理類(lèi)
package com.lyc.cn.v2.day04.proxy.cglib;

public class CglibCat {
    public void sayHello() {
        System.out.println("我是一只貓撩匕。。墨叛。");
    }
}
  • 自定義MethodInterceptor
package com.lyc.cn.v2.day04.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyCglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    // 這里的目標(biāo)類(lèi)型為Object止毕,則可以接受任意一種參數(shù)作為被代理類(lèi)并村,實(shí)現(xiàn)了動(dòng)態(tài)代理
    public Object getInstance(Class clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        // 返回代理對(duì)象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("==代理方法開(kāi)始執(zhí)行");
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println("==代理方法結(jié)束執(zhí)行");
        return result;
    }

}
  • 測(cè)試類(lèi)及結(jié)果
@Test
public void test4() {
    // CGLIB動(dòng)態(tài)代理
    CglibCat cat = (CglibCat) new MyCglibProxy().getInstance(CglibCat.class);
    cat.sayHello();
}
==代理方法開(kāi)始執(zhí)行
我是一只貓。滓技。。
==代理方法結(jié)束執(zhí)行

關(guān)于CGLIB的原理棚潦,經(jīng)過(guò)多種反編譯軟件測(cè)試令漂,反編譯出來(lái)的代碼好像不是正確的,而且跟網(wǎng)上其他的文章反編譯出來(lái)的有很大的不同丸边,不知道是不是因?yàn)閙acOS或CGLIB版本的原因叠必,這里就不在粘貼反編譯源碼了,如果感興趣的同學(xué)可以參考下面這篇文章妹窖,cglib原理解析纬朝。

5.總結(jié)

總而言之,動(dòng)態(tài)代理技術(shù)是AOP的基礎(chǔ)也是核心骄呼,大家一定要先把JDK共苛、CGLIB動(dòng)態(tài)代理搞明白之后,再去看AOP的源碼蜓萄,才能達(dá)到事半功倍的效果隅茎。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嫉沽,隨后出現(xiàn)的幾起案子辟犀,更是在濱河造成了極大的恐慌,老刑警劉巖绸硕,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堂竟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡玻佩,警方通過(guò)查閱死者的電腦和手機(jī)出嘹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咬崔,“玉大人疚漆,你說(shuō)我怎么就攤上這事〉笊猓” “怎么了娶聘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)甚脉。 經(jīng)常有香客問(wèn)我丸升,道長(zhǎng),這世上最難降的妖魔是什么牺氨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任狡耻,我火速辦了婚禮墩剖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夷狰。我一直安慰自己岭皂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布沼头。 她就那樣靜靜地躺著爷绘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪进倍。 梳的紋絲不亂的頭發(fā)上土至,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音猾昆,去河邊找鬼陶因。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垂蜗,可吹牛的內(nèi)容都是我干的楷扬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贴见,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼毅否!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蝇刀,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤螟加,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吞琐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捆探,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年站粟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黍图。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奴烙,死狀恐怖助被,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情切诀,我是刑警寧澤揩环,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站幅虑,受9級(jí)特大地震影響丰滑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜倒庵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一褒墨、第九天 我趴在偏房一處隱蔽的房頂上張望炫刷。 院中可真熱鬧,春花似錦郁妈、人聲如沸浑玛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顾彰。三九已至,卻和暖如春剧腻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涂屁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工书在, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拆又。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓儒旬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親帖族。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栈源,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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