Java代理模式和切面編程

1入蛆、代理模式

即Proxy Pattern撇眯,23種java常用設(shè)計(jì)模式之一谱仪。代理模式的定義:對(duì)其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)蓄髓。

1.1 介紹

代理模式的主要作用是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)。在某些情況下涯曲,一個(gè)對(duì)象不想或者不能直接引用另一個(gè)對(duì)象野哭,而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。
代理模式的思想是為了提供額外的處理或者不同的操作而在實(shí)際對(duì)象與調(diào)用者之間插入一個(gè)代理對(duì)象幻件。這些額外的操作通常需要與實(shí)際對(duì)象進(jìn)行通信拨黔。

1.2 場(chǎng)景舉例

假設(shè)有一組對(duì)象都實(shí)現(xiàn)同一個(gè)接口,實(shí)現(xiàn)同樣的方法绰沥,但這組對(duì)象中有一部分對(duì)象需要有單獨(dú)的方法篱蝇,傳統(tǒng)的笨辦法是在每一個(gè)應(yīng)用端都加上這個(gè)單獨(dú)的方法,但是代碼重用性低徽曲,耦合性高零截。如果用代理的方法則很好的解決了這個(gè)問(wèn)題。

1.3 涉及到的角色

代理模式是給指定對(duì)象提供代理對(duì)象秃臣。由代理對(duì)象來(lái)控制具體對(duì)象的引用涧衙。代理模式涉及到的角色如下:

  • 抽象主題角色
    聲明了代理主題和真實(shí)主題的公共接口,使任何需要真實(shí)主題的地方都能用代理主題代替奥此。
  • 代理主題角色
    含有真實(shí)主題的引用弧哎,從而可以在任何時(shí)候操作真實(shí)主題,代理主題功過(guò)提供和真實(shí)主題相同的接口稚虎,使它可以隨時(shí)代替真實(shí)主題撤嫩。代理主題通過(guò)持有真實(shí)主題的引用,不但可以控制真實(shí)主題的創(chuàng)建或刪除蠢终,可以在真實(shí)主題被調(diào)用前進(jìn)行攔截序攘,或在調(diào)用后進(jìn)行某些操作茴她。
  • 真實(shí)代理對(duì)象
    定義了代理角色所代表的具體對(duì)象。

1.4 分類(lèi)

按照代理創(chuàng)建的時(shí)期代理類(lèi)可以分成兩種:

  • 靜態(tài)代理
    由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼程奠,再對(duì)其編譯丈牢。在程序運(yùn)行前,代理類(lèi)的.class文件就已經(jīng)存在 了梦染。
  • 動(dòng)態(tài)代理
    在程序運(yùn)行時(shí)赡麦,運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成朴皆。

1.4.1 靜態(tài)代理的例子

接下來(lái)帕识,介紹一個(gè)代理模式使用的例子。在該例中遂铡,通過(guò)使用代理模式肮疗,沒(méi)有改動(dòng)Person類(lèi)的代碼,實(shí)現(xiàn)了給Person類(lèi)中方法添加日志的功能扒接。一方面伪货,如果有多個(gè)和Person類(lèi)似的實(shí)現(xiàn)了PersonInterface的類(lèi),都可以通過(guò)該代理類(lèi)實(shí)現(xiàn)日志功能钾怔。另一方面碱呼,也實(shí)現(xiàn)了業(yè)務(wù)代碼的解耦,Person類(lèi)專(zhuān)注于業(yè)務(wù)代碼的實(shí)現(xiàn)宗侦,可維護(hù)性和可讀性都更好愚臀。具體如下。
PersonInterface.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public interface PersonInterface {
    public void run();
    public void eat();
}

Person.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class Person implements PersonInterface {
    public Person(String name) {
        this.name = name;
    }

    private String name;

    public void run()
    {
        System.out.println(name + ": " + "i am running...");
    }

    public void eat()
    {
        System.out.println(name + ": " + "i am eating...");
    }

}

PersonInterfaceProxy.java:

package com.aop;

/**
 * Created by chengxia on 2019/4/1.
 */
public class PersonInterfaceProxy implements PersonInterface {

    PersonInterface p;

    public PersonInterfaceProxy(PersonInterface p) {
        this.p = p;
    }

    @Override
    public void eat() {
        System.out.println("Log: begin to eat.");
        p.eat();
        System.out.println("Log: eat over.");
    }

    @Override
    public void run() {
        System.out.println("Log: begin to run.");
        p.run();
        System.out.println("Log: run over.");
    }
}

TestProxy.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class TestProxy {
    public static void main(String []args){
        //新建一個(gè)person類(lèi)
        PersonInterface p = new Person("Kobe");
        //新建一個(gè)person代理
        PersonInterfaceProxy pProxy = new PersonInterfaceProxy(p);
        pProxy.eat();
        pProxy.run();
    }
}

運(yùn)行結(jié)果:

Log: begin to eat.
Kobe: i am eating...
Log: eat over.
Log: begin to run.
Kobe: i am running...
Log: run over.

Process finished with exit code 0

1.4.2 動(dòng)態(tài)代理介紹

由靜態(tài)代理的代碼可以看到靜態(tài)代理只能對(duì)一個(gè)接口進(jìn)行服務(wù)矾利,如果項(xiàng)目中有很多個(gè)接口姑裂,那么肯定會(huì)產(chǎn)生過(guò)多的代理。這時(shí)候就需要用到動(dòng)態(tài)代理男旗,由一個(gè)代理類(lèi)完成所有的代理功能舶斧。
動(dòng)態(tài)代理類(lèi)的字節(jié)碼在程序運(yùn)行時(shí)由Java反射機(jī)制動(dòng)態(tài)生成,無(wú)需程序員手工編寫(xiě)它的源代碼察皇。動(dòng)態(tài)代理類(lèi)不僅簡(jiǎn)化了編程工作茴厉,而且提高了軟件系統(tǒng)的可擴(kuò)展性,因?yàn)镴ava什荣。反射機(jī)制可以生成任意類(lèi)型的動(dòng)態(tài)代理類(lèi)矾缓。
JDK動(dòng)態(tài)代理中包含一個(gè)InvocationHandler接口和一個(gè)Proxy類(lèi)。
(1) invocationHandler接口

public interface InvocationHandler {     
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
}

其中:
Object porxy: 是被代理的對(duì)象
Method method: 要調(diào)用的方法
Object[] args: 要調(diào)用的方法的參數(shù)

(2)Proxy類(lèi)
Proxy類(lèi)是專(zhuān)門(mén)完成代理的操作類(lèi)溃睹,可以通過(guò)此類(lèi)為一個(gè)或多個(gè)接口動(dòng)態(tài)地生成實(shí)現(xiàn)類(lèi)而账,此類(lèi)提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,     InvocationHandler h)  throws IllegalArgumentException

其中:
ClassLoader loader: 是類(lèi)加載器(在java中主要有三種類(lèi)加載器:Booststrap ClassLoader:此加載器采用C++編寫(xiě),一般開(kāi)發(fā)中是看不到的因篇;Extendsion ClassLoader:用來(lái)進(jìn)行擴(kuò)展類(lèi)的加載泞辐,一般對(duì)應(yīng)的是jrelibext目錄中的類(lèi); AppClassLoader:(默認(rèn))加載classpath指定的類(lèi)笔横,是最常使用的是一種加載器。)
Class<?>[] interfaces: 得到全部接口;
InvocationHandler h: 得到InvocationHandler接口的子類(lèi)實(shí)例;

1.4.3 動(dòng)態(tài)代理例子

下面是個(gè)動(dòng)態(tài)代理的例子咐吼,原始的接口和類(lèi)用的還是上面例子中的吹缔。
DynamicProxyInnvocationHandler.java:

package com.aop;

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

/**
 * Created by chengxia on 2019/4/1.
 */
public class DynamicProxyInnvocationHandler implements InvocationHandler {
    private Object target;

    public DynamicProxyInnvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        //預(yù)處理邏輯
        System.out.println("Before executing " + method.getName());
        result = method.invoke(target, args);
        //事后處理邏輯
        System.out.println("After executing " + method.getName());
        return result;
    }
}

TestDynamicProxy.java:

package com.aop;

import java.lang.reflect.Proxy;

/**
 * Created by chengxia on 2019/3/30.
 */
public class TestDynamicProxy {
    public static void main(String []args){
        // 產(chǎn)生一個(gè)被代理對(duì)象,一個(gè)person類(lèi)
        PersonInterface p = new Person("Kobe");
        //
        // 將被代理對(duì)象交給InvocationHandler
        DynamicProxyInnvocationHandler dpi = new DynamicProxyInnvocationHandler(p);

        // 根據(jù)被代理對(duì)象產(chǎn)生一個(gè)代理
        PersonInterface pProxied = (PersonInterface) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), dpi);

        // 執(zhí)行被代理對(duì)象的方法
        pProxied.run();
        pProxied.eat();
    }
}

運(yùn)行結(jié)果如下:

Before executing run
Kobe: i am running...
After executing run
Before executing eat
Kobe: i am eating...
After executing eat

Process finished with exit code 0

2锯茄、切面編程介紹

上面提到的動(dòng)態(tài)代理厢塘,在java中最常見(jiàn)的應(yīng)用之一就是切面編程。
Aspect Oriented Programming(AOP)肌幽,面向切面編程晚碾,是一個(gè)比較熱門(mén)的話(huà)題。AOP主要實(shí)現(xiàn)的目的是針對(duì)業(yè)務(wù)處理過(guò)程中的切面進(jìn)行提取喂急,它所面對(duì)的是處理過(guò)程中的某個(gè)步驟或階段格嘁,以獲得邏輯過(guò)程中各部分之間低耦合性的隔離效果。

2.1 舉例介紹

比如我們最常見(jiàn)的就是日志記錄了廊移,舉個(gè)例子糕簿,我們現(xiàn)在提供一個(gè)查詢(xún)學(xué)生信息的服務(wù),但是我們希望記錄有誰(shuí)進(jìn)行了這個(gè)查詢(xún)狡孔。如果按照傳統(tǒng)的OOP的實(shí)現(xiàn)的話(huà)懂诗,那我們實(shí)現(xiàn)了一個(gè)查詢(xún)學(xué)生信息的服務(wù)接口(StudentInfoService)和其實(shí)現(xiàn)類(lèi) (StudentInfoServiceImpl.java),同時(shí)為了要進(jìn)行記錄的話(huà)苗膝,那我們?cè)趯?shí)現(xiàn)類(lèi)(StudentInfoServiceImpl.java)中要添加其實(shí)現(xiàn)記錄的過(guò)程殃恒。這樣的話(huà),假如我們要實(shí)現(xiàn)的服務(wù)有多個(gè)呢荚醒?那就要在每個(gè)實(shí)現(xiàn)的類(lèi)都添加這些記錄過(guò)程芋类。這樣做的話(huà)就會(huì)有點(diǎn)繁瑣,而且每個(gè)實(shí)現(xiàn)類(lèi)都與記錄服務(wù)日志的行為緊耦合界阁,違反了面向?qū)ο蟮囊?guī)則侯繁。
那么怎樣才能把記錄服務(wù)的行為與業(yè)務(wù)處理過(guò)程中分離出來(lái)呢?看起來(lái)好像就是查詢(xún)學(xué)生的服務(wù)自己在進(jìn)行泡躯,但卻是背后日志記錄對(duì)這些行為進(jìn)行記錄贮竟,并且查詢(xún)學(xué)生的服務(wù)不知道存在這些記錄過(guò)程,這就是我們要討論AOP的目的所在较剃。AOP的編程咕别,好像就是把我們?cè)谀硞€(gè)方面的功能提出來(lái)與一批對(duì)象進(jìn)行隔離,這樣與一批對(duì)象之間降低了耦合性写穴,可以就某個(gè)功能進(jìn)行編程惰拱。

2.2 實(shí)例:通過(guò)切面編程添加日志功能

通常,Java語(yǔ)言中的切面編程是通過(guò)動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的啊送。
接下來(lái)舉一個(gè)通過(guò)切面編程給一個(gè)類(lèi)添加日志功能的例子偿短。如下是一個(gè)Person類(lèi)欣孤,定義:
PersonInterface.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public interface PersonInterface {
    public void run();
    public void eat();
}

Person.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class Person implements PersonInterface {
    public Person(String name) {
        this.name = name;
    }

    private String name;

    public void run()
    {
        System.out.println(name + ": " + "i am running...");
    }

    public void eat()
    {
        System.out.println(name + ": " + "i am eating...");
    }

}

接下來(lái),通過(guò)切面編程昔逗,給該類(lèi)添加日志功能降传。
首先,編寫(xiě)日志記錄的代碼實(shí)現(xiàn):
LoggingInerface.java:

package com.aop;

import java.lang.reflect.Method;

/**
 * Created by chengxia on 2019/3/30.
 */
public interface LoggingInerface {
    public void log(Method m);
}

Logging.java:

package com.aop;

import java.lang.reflect.Method;

/**
 * Created by chengxia on 2019/3/30.
 */
public class Logging implements LoggingInerface {
    public void log(Method m){
        System.out.println("Log: " + m.getName() + "Method excuted.");
    }
}

接下來(lái)勾怒,通過(guò)java的動(dòng)態(tài)代理婆排,實(shí)現(xiàn)一個(gè)生成帶日志記錄功能Person實(shí)例的工廠(chǎng)方法:
PersonFactory.java:

package com.aop;

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

/**
 * Created by chengxia on 2019/3/30.
 */
public class PersonFactory {
    //接受目標(biāo)和建議,產(chǎn)生任意類(lèi)(只要該類(lèi)有接口)的代理類(lèi),攔截所有的方法訪(fǎng)問(wèn)
    public static Object getPerson(final Object obj,final LoggingInerface log)
    {
        Object proxy = Proxy.newProxyInstance(PersonFactory.class.getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler()
        {
            public Object invoke(Object arg0, Method m, Object[] arg2)
                    throws Throwable
            {
                log.log(m);
                Object value = m.invoke(obj, arg2);
                return value;
            }
        });

        return proxy;
    }
}

到這里笔链,切面程序就完成了段只,接下來(lái),寫(xiě)一個(gè)類(lèi)測(cè)試:
TestAOP.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class TestAOP {
    public static void main(String []args){
        //包含日志記錄的類(lèi)
        LoggingInerface log = new Logging();
        PersonInterface p = (PersonInterface) PersonFactory.getPerson(new Person("Tom"), log);
        p.eat();
        p.eat();
        p.run();
        p.run();
    }
}

運(yùn)行結(jié)果如下:

Log: eatMethod excuted.
Tom: i am eating...
Log: eatMethod excuted.
Tom: i am eating...
Log: runMethod excuted.
Tom: i am running...
Log: runMethod excuted.
Tom: i am running...

Process finished with exit code 0

這時(shí)候卡乾,如果我們新建一個(gè)類(lèi)翼悴,也實(shí)現(xiàn)了PersonInterface接口:
People.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class People implements PersonInterface {
    public People(String name) {
        this.name = name;
    }

    private String name;

    public void run()
    {
        System.out.println(name + ": " + "i am running...");
    }

    public void eat()
    {
        System.out.println(name + ": " + "i am eating...");
    }

}

這個(gè)類(lèi)也可以通過(guò)前面的動(dòng)態(tài)代理,獲得日志功能幔妨。下面是一個(gè)測(cè)試?yán)樱?br> TestAOP.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class TestAOP {
    public static void main(String []args){
        //包含日志記錄的類(lèi)
        LoggingInerface log = new Logging();
        PersonInterface p = (PersonInterface) PersonFactory.getPerson(new People("Chairman"), log);
        p.eat();
        p.eat();
        p.run();
        p.run();
    }
}

運(yùn)行結(jié)果:

Log: eatMethod excuted.
Chairman: i am eating...
Log: eatMethod excuted.
Chairman: i am eating...
Log: runMethod excuted.
Chairman: i am running...
Log: runMethod excuted.
Chairman: i am running...

Process finished with exit code 0

甚至這里,我們?cè)傩陆ㄒ粋€(gè)Animal類(lèi)谍椅,定義如下:
AnimalInterface.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public interface AnimalInterface {
    public void jump();
    public void fly();
}

Animal.java:

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class Animal implements AnimalInterface {
    public Animal(String name) {
        this.name = name;
    }

    private String name;

    public void jump()
    {
        System.out.println(name + ": " + "jumping...");
    }

    public void fly()
    {
        System.out.println(name + ": " + "flying...");
    }

}

這個(gè)類(lèi)误堡,也可以通過(guò)上面的動(dòng)態(tài)代理切面獲得日志功能,下面是要給測(cè)試?yán)樱?/p>

package com.aop;

/**
 * Created by chengxia on 2019/3/30.
 */
public class TestAOP {
    public static void main(String []args){
        //包含日志記錄的類(lèi)
        LoggingInerface log = new Logging();
        AnimalInterface a = (AnimalInterface) PersonFactory.getPerson(new Animal("Paopao"), log);
        a.fly();
        a.jump();
    }
}

運(yùn)行結(jié)果如下:

Log: flyMethod excuted.
Paopao: flying...
Log: jumpMethod excuted.
Paopao: jumping...

Process finished with exit code 0

可見(jiàn)雏吭,切面還是很強(qiáng)大的锁施。它可以實(shí)現(xiàn)業(yè)務(wù)代碼和技術(shù)日志代碼的分離,使代碼的結(jié)構(gòu)杖们、可讀性悉抵、可維護(hù)性等都更好。

面向切面在英文中的單詞是Aspect Oriented Programming(AOP)摘完,在spring框架中叫aop姥饰,它是可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。它是一種新的方法論孝治,它是對(duì)傳統(tǒng)OOP編程的一種補(bǔ)充列粪。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谈飒,隨后出現(xiàn)的幾起案子岂座,更是在濱河造成了極大的恐慌,老刑警劉巖杭措,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件费什,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡手素,警方通過(guò)查閱死者的電腦和手機(jī)鸳址,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)赘那,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人氯质,你說(shuō)我怎么就攤上這事募舟。” “怎么了闻察?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵拱礁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我辕漂,道長(zhǎng)呢灶,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任钉嘹,我火速辦了婚禮鸯乃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跋涣。我一直安慰自己缨睡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布陈辱。 她就那樣靜靜地躺著奖年,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沛贪。 梳的紋絲不亂的頭發(fā)上陋守,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音利赋,去河邊找鬼水评。 笑死,一個(gè)胖子當(dāng)著我的面吹牛媚送,可吹牛的內(nèi)容都是我干的中燥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼季希,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼褪那!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起式塌,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤博敬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后峰尝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體偏窝,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祭往。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伦意。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖硼补,靈堂內(nèi)的尸體忽然破棺而出驮肉,到底是詐尸還是另有隱情,我是刑警寧澤已骇,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布离钝,位于F島的核電站,受9級(jí)特大地震影響褪储,放射性物質(zhì)發(fā)生泄漏卵渴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一鲤竹、第九天 我趴在偏房一處隱蔽的房頂上張望浪读。 院中可真熱鬧,春花似錦辛藻、人聲如沸碘橘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛹屿。三九已至,卻和暖如春岩榆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坟瓢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工勇边, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人折联。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓粒褒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诚镰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奕坟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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