順著dubbo入口擼ExtensionLoader源碼

參考

屬于在這篇Dubbo擴(kuò)展點(diǎn)加載基礎(chǔ)上的展開(kāi)學(xué)習(xí)耍攘。但原文有點(diǎn)小問(wèn)題(Container啟動(dòng)那里)霜大,所以本文直接按自己的理解來(lái)組織癞谒。不加說(shuō)明的引用都來(lái)自該文啊掏。

問(wèn)題

Dubbo的原理梗搅、核心的概念很多文章都有詳細(xì)的介紹(什么SPI禾唁、擴(kuò)展點(diǎn)效览、Adaptive、ExtensionLoader等等)荡短,但是我的問(wèn)題是它們是如何運(yùn)行丐枉、如何起作用、整體的流程是什么掘托?

1. Dubbo入口

執(zhí)行Dubbo瘦锹,實(shí)際上是執(zhí)行了com.alibaba.dubbo.container.Main.main

Main這個(gè)類(lèi)中有一些靜態(tài)成員變量,最重要的是這個(gè):

private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

從這里開(kāi)始使用ExtensionLoader闪盔。

2. 初始化Main的靜態(tài)成員弯院,獲得Container類(lèi)型的ExtensionLoader

ExtensionLoader類(lèi)中有個(gè)靜態(tài)的final變量EXTENSION_LOADERS緩存了各種類(lèi)型的ExtensionLoader,如果已有緩存泪掀,就直接獲取听绳,如果沒(méi)有就調(diào)用new方法新建。

啟動(dòng)時(shí)getExtensionLoader(Container.class)獲取Container類(lèi)型的ExtensionLoader肯定不存在异赫,去構(gòu)造:

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

ExtensionLoader的構(gòu)造方法中可以看到椅挣,ExtensionLoader有兩個(gè)屬性,一個(gè)是type塔拳,標(biāo)識(shí)了這個(gè)ExtensionLoader所屬的類(lèi)型鼠证,另一個(gè)是ObjectFactory,類(lèi)型是ExtensionFactory靠抑。

那么量九,ExtensionLoader<Container>this.type=Container.class
而根據(jù)構(gòu)造函數(shù)颂碧,欲要得到Container.ClassObjectFactory娩鹉,須先得到ExtensionFactory類(lèi)型的ExtensionLoader

2.1 獲取ExtensionFactory類(lèi)型的ExtensionLoader

同樣沒(méi)有稚伍,去構(gòu)造弯予,根據(jù)構(gòu)造函數(shù)ExtensionLoader<ExtensionFactory>this.type=ExtensionFactory.ClassObjectFactory=null个曙。
繼續(xù)锈嫩。

2.2 獲取ExtensionFactory的擴(kuò)展實(shí)現(xiàn):getAdaptiveExtension()

Dubbo的微內(nèi)核做得非常的徹底,ExtensionFactory也是一個(gè)擴(kuò)展點(diǎn)垦搬,也需要通過(guò)ExtensionLoader<ExtensionFactory>加載

因?yàn)椋?code>Container的)ObjectFactoryExtensionFactory類(lèi)型呼寸,而ExtensionFactory是一個(gè)擴(kuò)展點(diǎn),那么就要用ExtensionFactory.ClassExtensionLoader通過(guò)getAdaptiveExtension()獲取到ExtensionFactory.Class的Adaptive實(shí)現(xiàn)猴贰,再給到(Container的)ObjectFactory对雪。

getAdaptiveExtension()中,首先也是試圖從ExtensionLoader<ExtensionFactory>的一個(gè)緩存cachedAdaptiveInstance中取米绕,取不到就調(diào)用createAdaptiveExtension()創(chuàng)建瑟捣。

2.2.1 創(chuàng)建ExtensionFactory擴(kuò)展: createAdaptiveExtension()

邏輯就一句:

injectExtension((T) getAdaptiveExtensionClass().newInstance());

先看 getAdaptiveExtensionClass()馋艺。

2.2.1.1 獲取ExtensionFactory擴(kuò)展類(lèi):getAdaptiveExtensionClass()

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

這里整體的邏輯是,先getExtensionClasses()迈套,看看有沒(méi)有已經(jīng)存在的擴(kuò)展實(shí)現(xiàn)類(lèi)捐祠,有的話(huà)取到這些類(lèi),把標(biāo)記了@Adaptive的緩存到cachedAdaptiveClass(比如說(shuō)ExtensionFactory就是有的)桑李,把沒(méi)有標(biāo)記@Adaptive的擴(kuò)展實(shí)現(xiàn)類(lèi)緩存到cachedClasses踱蛀,返回。

如果沒(méi)有的話(huà)贵白,就調(diào)用createAdaptiveExtensionClass()現(xiàn)生成code并compile一個(gè)Adaptive類(lèi)出來(lái)率拒,同樣緩存到cachedAdaptiveClass,返回禁荒。

Adaptive注解可以標(biāo)記在類(lèi)上猬膨,也可以標(biāo)記在方法上。
如果Adaptive類(lèi)選擇擴(kuò)展點(diǎn)實(shí)現(xiàn)的依據(jù)是根據(jù)上下文信息圈浇,即URL中的信息選擇不同的實(shí)現(xiàn),那么可以把Adaptive注解標(biāo)記在服務(wù)的方法上靴寂,ExtensionLoader可以自動(dòng)生成這種情況的Adaptive類(lèi)磷蜀。
如果是自己實(shí)現(xiàn)Adaptive類(lèi),那么需要在類(lèi)上標(biāo)記Adaptive注解百炬,ExtensionLoader在加載擴(kuò)展點(diǎn)時(shí)就能發(fā)現(xiàn)并創(chuàng)建Adaptive實(shí)現(xiàn)褐隆。

(來(lái)自Dubbo源碼解析-2:Adaptive類(lèi),同樣很厲害的文章嗯剖踊,本文對(duì)其也多有引用不一一列舉了)

下面詳細(xì)分析一下這兩步庶弃。

2.2.1.1.1 獲取已存在的擴(kuò)展類(lèi):getExtensionClasses()

首先檢查ExtensionLoader<ExtensionFactory>的緩存cachedClasses,有就get沒(méi)有就調(diào)用loadExtensionClasses()創(chuàng)建德澈。

  • loadExtensionClasses()
    首先去拿SPI注解歇攻,如果注解不為null,獲取注解的value:
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
...
String value = defaultAnnotation.value();

ExtensionFactory定義如下:

@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     * 
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

SPI注解類(lèi)定義如下:

public @interface SPI {

    /**
     * 缺省擴(kuò)展點(diǎn)名梆造。
     */
    String value() default "";

}

可以看到ExtensionFactory沒(méi)有定義缺省擴(kuò)展點(diǎn)名(后面會(huì)看到Container類(lèi)有缺省定義)缴守。

有缺省擴(kuò)展點(diǎn)名就緩存到ExtensionLoader<ExtensionFactory>的另一個(gè)緩存cachedDefaultName,沒(méi)有就繼續(xù)镇辉。

繼續(xù)屡穗,調(diào)用loadFile裝載擴(kuò)展實(shí)現(xiàn)類(lèi)。

Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
  • loadFile(Map<String, Class<?>> extensionClasses, String dir)

loadFile首先在指定目錄下找名字是type的配置文件忽肛,然后讀出來(lái):

String fileName = dir + type.getName();

對(duì) 于 ExtensionFactory來(lái) 說(shuō) 村砂, 會(huì)讀到:

//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

隨后:

  1. loadfile讀入第一行數(shù)據(jù),初始化:name=adaptive, line = com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory屹逛。

  2. 使用Class<?> clazz = Class.forName(line, true, classLoader)加載類(lèi)础废。

  3. !type.isAssignableFrom(clazz):驗(yàn)證加載的類(lèi)是否是當(dāng)前type的一個(gè)實(shí)現(xiàn)汛骂。

  4. clazz.isAnnotationPresent(Adaptive.class):檢查如果設(shè)置了@Adaptive,則把clazz保存在cachedAdaptiveClass
    對(duì)于ExtensionFactory來(lái)說(shuō)色迂,AdaptiveExtensionFactory設(shè)置了@Adaptive注解:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
  1. 對(duì)于沒(méi)有設(shè)置@Adaptive的類(lèi)香缺,則存入loadExtensionClasses()傳到loadFile()中的參數(shù)extensionClasses,返回后在getExtensionClasses() 中賦給cachedClasses緩存歇僧。
classes = loadExtensionClasses();
cachedClasses.set(classes);

對(duì)于ExtensionFactory來(lái)說(shuō)图张,SpiExtensionFactorySpringExtensionFactory都沒(méi)有設(shè)置@Adaptive注解(同一個(gè)類(lèi)只能有一個(gè)Adaptive實(shí)現(xiàn)),所以都被存入了ExtensionLoader<ExtensionFactory>cachedClasses诈悍。

2.2.1.1.2 動(dòng)態(tài)生成沒(méi)有自己實(shí)現(xiàn)的Adaptive類(lèi):createAdaptiveExtensionClass()

執(zhí)行完getExtensionClasses()祸轮,回到了2.2.1.1的getAdaptiveExtensionClass(),如果此時(shí)cachedAdaptiveClass仍為null侥钳,說(shuō)明沒(méi)有找到標(biāo)記了@Adaptive的類(lèi)适袜,需要根據(jù)上下文動(dòng)態(tài)生成。

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

動(dòng)態(tài)生成的過(guò)程簡(jiǎn)單來(lái)說(shuō)就是找到標(biāo)記了@Adaptive的方法舷夺,根據(jù)type通過(guò)StringBuilder拼接一個(gè)類(lèi)苦酱,加載Compiler編譯。

前面提到的參考文章(Dubbo源碼解析-2:Adaptive類(lèi))里舉了例子给猾,可以看到生成了什么樣子的代碼疫萤。這里不詳細(xì)說(shuō)了。

2.2.1.2 為擴(kuò)展注入依賴(lài)的其他擴(kuò)展實(shí)現(xiàn):injectExtension(T instance)

好的終于回來(lái)了敢伸,2.2.1.1節(jié)完成了getAdaptiveExtensionClass()扯饶,并返回了cachedAdaptiveClass。 為這個(gè)cachedAdaptiveClassnew了一個(gè)Instance以后池颈,開(kāi)始injectExtension注入尾序。

擴(kuò)展點(diǎn)注入的代碼如下,首先查找以set開(kāi)頭躯砰、帶一個(gè)參數(shù)的public方法每币,如果set方法的名稱(chēng)大于3,則根據(jù)名稱(chēng)獲取參數(shù)擴(kuò)展點(diǎn)的名稱(chēng)琢歇,然后獲取擴(kuò)展點(diǎn)實(shí)現(xiàn)的實(shí)例脯爪,這可能又是一個(gè)次配置的解析和實(shí)例化過(guò)程。最后調(diào)用set方法將實(shí)例設(shè)置進(jìn)去矿微。

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")&&...) {
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            ...
                        }
                    }
                }
            }
        } catch (Exception e) {
           ...
        }
        return instance;
    }

對(duì)于ExtensionFactory來(lái)說(shuō)痕慢,首先判斷,if (objectFactory != null)涌矢,則可以直接返回cachedAdaptiveClass所new的instance了掖举。

2.2.2 ExtensionLoader<Container>構(gòu)造完畢

整個(gè)2.2.1過(guò)程結(jié)束,拿到了ExtensionFactory.class的擴(kuò)展實(shí)現(xiàn)娜庇,也就是所返回的cachedAdaptiveClass塔次,即AdaptiveExtensionFactory方篮。

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

回到構(gòu)造函數(shù),ExtensionLoader<Container>objectFactory =AdaptiveExtensionFactory励负。

ExtensionLoader<Container>構(gòu)造完畢藕溅。

3. 在main()中調(diào)用loader.getExtension()加載Container的擴(kuò)展實(shí)現(xiàn)

Main函數(shù)的整體邏輯是,如果沒(méi)有傳入?yún)?shù)继榆,就裝載Container的SPI注解指定的默認(rèn)Container巾表,如果傳入的參數(shù)(main函數(shù)參數(shù)、JVM啟動(dòng)參數(shù)略吨、classpath下的dubbo.properties配置等)指定了Container集币,就裝載通過(guò)參數(shù)指定的Container。

public class Main {
    public static final String CONTAINER_KEY = "dubbo.container";
    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
    ...    
    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
            
            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i ++) {
                containers.add(loader.getExtension(args[i]));
            }
            ...
            for (Container container : containers) {
                container.start();
               ...
            }
            ...
    }
    
}

首先看Container類(lèi)的定義:

@SPI("spring")
public interface Container {
    
    /**
     * start.
     */
    void start();
    
    /**
     * stop.
     */
    void stop();

}

它將SPI的value值設(shè)置Spring翠忠,也就是指定默認(rèn)的擴(kuò)展實(shí)現(xiàn)名稱(chēng)是spring(默認(rèn)情況只加載一個(gè)spring容器)鞠苟。

再看關(guān)于Container的配置(以spring為例,其他jetty秽之、log4j等都類(lèi)似):

\\META-INF/dubbo/internal/com.alibaba.dubbo.container.Container
spring=com.alibaba.dubbo.container.spring.SpringContainer

而SpringContainer類(lèi)沒(méi)有加注解什么的当娱,所以這里是通過(guò)SPI注解的默認(rèn)值控制的,和ExtensionFactory的機(jī)制不同考榨。

下面詳細(xì)分析main函數(shù)加載Container擴(kuò)展點(diǎn)過(guò)程跨细。

3.1 如果沒(méi)有傳參,獲得默認(rèn)擴(kuò)展名:loader.getDefaultExtensionName()

    public String getDefaultExtensionName() {
        getExtensionClasses();
        return cachedDefaultName;
    }

這里調(diào)用了getExtensionClasses()董虱,與2.2.1.1.1節(jié)介紹的過(guò)程相同扼鞋。

不同的是申鱼,對(duì)于ExtensionLoader<Container>愤诱,通過(guò)SPI注解定義了缺省擴(kuò)展點(diǎn)名spring,因此會(huì)將spring緩存到cachedDefaultName捐友。

而由于Container的擴(kuò)展實(shí)現(xiàn)類(lèi)都沒(méi)有設(shè)置@Adaptive淫半,則這些實(shí)現(xiàn)類(lèi)都被緩存在cachedClasses中,不會(huì)被緩存在cachedAdaptiveClass匣砖。

3.2 獲得Container:loader.getExtension()

  • getExtension()
    先去緩存cachedInstances中找科吭,如果沒(méi)有,則調(diào)用createExtension創(chuàng)建猴鲫。
public T getExtension(String name) {
        ...
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
  • createExtension()
    通過(guò)getExtensionClasses()獲得擴(kuò)展實(shí)現(xiàn)類(lèi)对人,調(diào)用injectExtension注入依賴(lài)的其他擴(kuò)展和包裝類(lèi)。返回獲取了包裝后的擴(kuò)展instance拂共。
private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        ...
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
           ...
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            ...
        }
    }
  • getExtensionClasses()

其實(shí)是從獲取之前l(fā)oad到cachedClassed中的所有擴(kuò)展實(shí)現(xiàn)類(lèi)牺弄。沒(méi)有的話(huà)就調(diào)用loadExtensionClasses()(見(jiàn)2.2.1.1.1節(jié))獲取。

private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            ...
            classes = loadExtensionClasses();
            ...
        }
        return classes;
    }

3.3 獲取Container擴(kuò)展后宜狐,啟動(dòng)

擴(kuò)展加載結(jié)束势告,啟動(dòng)Container~

for (Container container : containers) {
       container.start();
       ...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛇捌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咱台,更是在濱河造成了極大的恐慌络拌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回溺,死亡現(xiàn)場(chǎng)離奇詭異春贸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)馅而,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)祥诽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓮恭,你說(shuō)我怎么就攤上這事雄坪。” “怎么了屯蹦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵维哈,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我登澜,道長(zhǎng)阔挠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任脑蠕,我火速辦了婚禮购撼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谴仙。我一直安慰自己迂求,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布晃跺。 她就那樣靜靜地躺著揩局,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀虎。 梳的紋絲不亂的頭發(fā)上凌盯,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音烹玉,去河邊找鬼驰怎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛二打,可吹牛的內(nèi)容都是我干的县忌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芹枷!你這毒婦竟也來(lái)了衅疙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鸳慈,失蹤者是張志新(化名)和其女友劉穎饱溢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體走芋,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绩郎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翁逞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肋杖。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挖函,靈堂內(nèi)的尸體忽然破棺而出状植,到底是詐尸還是另有隱情,我是刑警寧澤怨喘,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布津畸,位于F島的核電站,受9級(jí)特大地震影響必怜,放射性物質(zhì)發(fā)生泄漏肉拓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一梳庆、第九天 我趴在偏房一處隱蔽的房頂上張望暖途。 院中可真熱鬧,春花似錦膏执、人聲如沸驻售。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芋浮。三九已至抱环,卻和暖如春壳快,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镇草。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工眶痰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梯啤。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓竖伯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子七婴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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