深入探究jar包沖突

Jar包沖突的本質(zhì)是什么?
Java應(yīng)用程序因某種因素,加載不到正確的類而導(dǎo)致其行為跟預(yù)期不一致。

具體來說可分為兩種情況:

  • 應(yīng)用程序依賴的同一個(gè)Jar包出現(xiàn)了多個(gè)不同版本,并選擇了錯(cuò)誤的版本而導(dǎo)致JVM加載不到需要的類或加載了錯(cuò)誤版本的類坝辫,為了敘述的方便,暫且稱之為第一類Jar包沖突問題射亏;
  • 同樣的類(類的全限定名完全一樣)出現(xiàn)在多個(gè)不同的依賴Jar包中近忙,即該類有多個(gè)版本,并由于Jar包加載的先后順序?qū)е翵VM加載了錯(cuò)誤版本的類智润,稱之為第二類Jar包問題及舍。這兩種情況所導(dǎo)致的結(jié)果其實(shí)是一樣的,都會(huì)使應(yīng)用程序加載不到正確的類窟绷,那其行為自然會(huì)跟預(yù)期不一致了锯玛,以下對(duì)這兩種類型進(jìn)行詳細(xì)分析。

同一個(gè)Jar包出現(xiàn)了多個(gè)不同版本

隨著Jar包迭代升級(jí),我們所依賴的開源的或公司內(nèi)部的Jar包工具都會(huì)存在若干不同的版本攘残,而版本升級(jí)自然就避免不了類的方法簽名變更拙友,甚至于類名的更替,而我們當(dāng)前的應(yīng)用程序往往依賴特定版本的某個(gè)類 M 歼郭,由于maven的傳遞依賴而導(dǎo)致同一個(gè)Jar包出現(xiàn)了多個(gè)版本遗契,當(dāng)maven的仲裁機(jī)制選擇了錯(cuò)誤的版本時(shí),而恰好類 M在該版本中被去掉了病曾,或者方法簽名改了牍蜂,導(dǎo)致應(yīng)用程序因找不到所需的類 M或找不到類 M中的特定方法,就會(huì)出現(xiàn)第一類Jar沖突問題知态。
可總結(jié)出該類沖突問題發(fā)生的以下三個(gè)必要條件:

  • 由于maven的傳遞依賴導(dǎo)致依賴樹中出現(xiàn)了同一個(gè)Jar包的多個(gè)版本
  • 該Jar包的多個(gè)版本之間存在接口差異捷兰,如類名更替立叛,方法簽名更替等负敏,且應(yīng)用程序依賴了其中有變更的類或方法
  • maven的仲裁機(jī)制選擇了錯(cuò)誤的版本

同一個(gè)類出現(xiàn)在多個(gè)不同Jar包中

同樣的類出現(xiàn)在了應(yīng)用程序所依賴的兩個(gè)及以上的不同Jar包中,這會(huì)導(dǎo)致什么問題呢秘蛇?我們知道其做,同一個(gè)類加載器對(duì)于同一個(gè)類只會(huì)加載一次,那么當(dāng)一個(gè)類出現(xiàn)在了多個(gè)Jar包中赁还,假設(shè)有 A 妖泄、 B 、 C 等艘策,由于Jar包依賴的路徑長短蹈胡、聲明的先后順序或文件系統(tǒng)的文件加載順序等原因,類加載器首先從Jar包 A 中加載了該類后朋蔫,就不會(huì)加載其余Jar包中的這個(gè)類了罚渐,那么問題來了:如果應(yīng)用程序此時(shí)需要的是Jar包 B 中的類版本,并且該類在Jar包 A 和 B 中有差異(方法不同驯妄、成員不同等等)荷并,而JVM卻加載了Jar包 A 的中的類版本,與期望不一致青扔,自然就會(huì)出現(xiàn)各種詭異的問題源织。
  從上面的描述中,可以發(fā)現(xiàn)出現(xiàn)不同Jar包的沖突問題有以下三個(gè)必要條件:

  • 同一個(gè)類 M 出現(xiàn)在了多個(gè)依賴的Jar包中微猖,為了敘述方便谈息,假設(shè)還是兩個(gè): A 和 B
  • Jar包 A 和 B 中的該類 M 有差異,無論是方法簽名不同也好凛剥,成員變量不同也好黎茎,只要可以造成實(shí)際加載的類的行為和期望不一致都行。如果說Jar包 A 和 B 中的該類完全一樣当悔,那么類加載器無論先加載哪個(gè)Jar包傅瞻,得到的都是同樣版本的類 M 踢代,不會(huì)有任何影響,也就不會(huì)出現(xiàn)Jar包沖突帶來的詭異問題嗅骄。
  • 加載的類 M 不是所期望的版本胳挎,即加載了錯(cuò)誤的Jar包

沖突的產(chǎn)生原因

maven仲裁機(jī)制

當(dāng)前maven大行其道,說到第一類Jar包沖突問題的產(chǎn)生原因溺森,就不得不提maven的依賴機(jī)制傳遞性依賴是Maven2.0引入的新特性慕爬,讓我們只需關(guān)注直接依賴的Jar包,對(duì)于間接依賴的Jar包屏积,Maven會(huì)通過解析從遠(yuǎn)程倉庫獲取的依賴包的pom文件來隱式地將其引入医窿,這為我們開發(fā)帶來了極大的便利,但與此同時(shí)炊林,也帶來了常見的問題——版本沖突姥卢,即同一個(gè)Jar包出現(xiàn)了多個(gè)不同的版本,針對(duì)該問題Maven也有一套仲裁機(jī)制來決定最終選用哪個(gè)版本渣聚,但Maven的選擇往往不一定是我們所期望的独榴,這也是產(chǎn)生Jar包沖突最常見的原因之一。先來看下Maven的仲裁機(jī)制:

  • 優(yōu)先按照依賴管理<dependencyManagement>元素中指定的版本聲明進(jìn)行仲裁奕枝,此時(shí)下面的兩個(gè)原則都無效了
  • 若無版本聲明棺榔,則按照“短路徑優(yōu)先”的原則(Maven2.0)進(jìn)行仲裁,即選擇依賴樹中路徑最短的版本
  • 若路徑長度一致隘道,則按照“第一聲明優(yōu)先”的原則進(jìn)行仲裁症歇,即選擇POM中最先聲明的版本

從maven的仲裁機(jī)制中可以發(fā)現(xiàn),除了第一條仲裁規(guī)則(這也是解決Jar包沖突的常用手段之一)外谭梗,后面的兩條原則忘晤,對(duì)于同一個(gè)Jar包不同版本的選擇,maven的選擇有點(diǎn)“一廂情愿”了默辨,也許這是maven研發(fā)團(tuán)隊(duì)在總結(jié)了大量的項(xiàng)目依賴管理經(jīng)驗(yàn)后得出的兩條結(jié)論德频,又或者是發(fā)現(xiàn)根本找不到一種統(tǒng)一的方式來滿足所有場景之后的無奈之舉,可能這對(duì)于多數(shù)場景是適用的缩幸,但是它不一定適合我——當(dāng)前的應(yīng)用壹置,因?yàn)槊總€(gè)應(yīng)用都有其特殊性,該依賴哪個(gè)版本表谊,maven沒辦法幫你完全搞定钞护,如果你沒有規(guī)規(guī)矩矩地使用<dependencyManagement>來進(jìn)行依賴管理,就注定了逃脫不了第一類Jar包沖突問題爆办。
例子
如果項(xiàng)目的依賴A和依賴B同時(shí)引入了依賴C难咕。
如果依賴C在A和B中的版本不一致就可能依賴沖突。
比如 項(xiàng)目 <- A, B, A <- C(1.0)余佃,B <- C(1.1)暮刃。
那么maven如果選擇高版本C(1.1)來導(dǎo)入(這個(gè)選擇maven會(huì)根據(jù)不等路徑短路徑原則和同等路徑第一聲明原則選取),C(1.0)中的類c在C(1.1)中被修改而不存在了爆土。
在編譯期可能并不會(huì)報(bào)錯(cuò)椭懊,因?yàn)榫幾g的目的只是把業(yè)務(wù)源代碼編譯成class文件,所以如果項(xiàng)目源代碼中沒有引入共有依賴C因升級(jí)而缺失的類c步势,就不會(huì)出現(xiàn)編譯失敗氧猬。除非源代碼就引入了共有依賴C因升級(jí)而缺失的類c則會(huì)直接編譯失敗
在運(yùn)行期坏瘩,很有可能出現(xiàn)依賴A在執(zhí)行過程中調(diào)用C(1.0)以前有但是升級(jí)到C(1.1)就缺失的類c盅抚,導(dǎo)致運(yùn)行期失敗,出現(xiàn)很典型的依賴沖突時(shí)的NoClassDefFoundError錯(cuò)誤倔矾。
如果是升級(jí)后出現(xiàn)原有的方法被修改而不存在的情況時(shí)妄均,就會(huì)拋出NoSuchMethodError錯(cuò)誤

Jar包的加載順序

對(duì)于第二類Jar包沖突問題,即多個(gè)不同的Jar包有類沖突破讨,這相對(duì)于第一類問題就顯得更為棘手丛晦。為什么這么說呢奕纫?在這種情況下提陶,兩個(gè)不同的Jar包,假設(shè)為 A匹层、 B隙笆,它們的名稱互不相同,甚至可能完全不沾邊升筏,如果不是出現(xiàn)沖突問題撑柔,你可能都不會(huì)發(fā)現(xiàn)它們有共有的類!對(duì)于A您访、B這兩個(gè)Jar包铅忿,maven就顯得無能為力了,因?yàn)閙aven只會(huì)為你針對(duì)同一個(gè)Jar包的不同版本進(jìn)行仲裁灵汪,而這倆是屬于不同的Jar包檀训,超出了maven的依賴管理范疇。此時(shí)享言,當(dāng)A峻凫、B都出現(xiàn)在應(yīng)用程序的類路徑下時(shí),就會(huì)存在潛在的沖突風(fēng)險(xiǎn)览露,即A荧琼、B的加載先后順序就決定著JVM最終選擇的類版本,如果選錯(cuò)了,就會(huì)出現(xiàn)詭異的第二類沖突問題命锄。

那么Jar包的加載順序都由哪些因素決定的呢堰乔?具體如下:

  • Jar包所處的加載路徑,或者換個(gè)說法就是加載該Jar包的類加載器在JVM類加載器樹結(jié)構(gòu)中所處層級(jí)脐恩。由于JVM類加載的雙親委派機(jī)制浩考,層級(jí)越高的類加載器越先加載其加載路徑下的類,顧名思義被盈,引導(dǎo)類加載器(bootstrap ClassLoader析孽,也叫啟動(dòng)類加載器)是最先加載其路徑下Jar包的,其次是擴(kuò)展類加載器(extension ClassLoader)只怎,再次是系統(tǒng)類加載器(system ClassLoader袜瞬,也就是應(yīng)用加載器appClassLoader),Jar包所處加載路徑的不同身堡,就決定了它的加載順序的不同邓尤。
  • 文件系統(tǒng)的文件加載順序。這個(gè)因素很容易被忽略贴谎,而往往又是因環(huán)境不一致而導(dǎo)致各種詭異沖突問題的罪魁禍?zhǔn)坠R騮omcat、resin等容器的ClassLoader獲取加載路徑下的文件列表時(shí)是不排序的擅这,這就依賴于底層文件系統(tǒng)返回的順序澈魄,那么當(dāng)不同環(huán)境之間的文件系統(tǒng)不一致時(shí),就會(huì)出現(xiàn)有的環(huán)境沒問題仲翎,有的環(huán)境出現(xiàn)沖突痹扇。例如,對(duì)于Linux操作系統(tǒng)溯香,返回順序則是由iNode的順序來決定的鲫构,如果說測試環(huán)境的Linux系統(tǒng)與線上環(huán)境不一致時(shí),就極有可能出現(xiàn)典型案例:測試環(huán)境怎么測都沒問題玫坛,但一上線就出現(xiàn)沖突問題结笨,規(guī)避這種問題的最佳辦法就是盡量保證測試環(huán)境與線上一致。

沖突的表象

Jar包沖突可能會(huì)導(dǎo)致哪些問題湿镀?通常發(fā)生在編譯或運(yùn)行時(shí)炕吸,主要分為兩類問題:一類是比較直觀的也是最為常見的錯(cuò)誤是拋出各種運(yùn)行時(shí)異常,還有一類就是比較隱晦的問題肠骆,它不會(huì)報(bào)錯(cuò)算途,其表現(xiàn)形式是應(yīng)用程序的行為跟預(yù)期不一致,分條羅列如下:

  • java.lang.ClassNotFoundException蚀腿,即java類找不到嘴瓤。這類典型異常通常是由于扫外,沒有在依賴管理中聲明版本,maven的仲裁的時(shí)候選取了錯(cuò)誤的版本廓脆,而這個(gè)版本缺少我們需要的某個(gè)class而導(dǎo)致該錯(cuò)誤筛谚。例如httpclient-4.4.jar升級(jí)到httpclient-4.36.jar時(shí),類org.apache.http.conn.ssl.NoopHostnameVerifier被去掉了停忿,如果此時(shí)我們本來需要的是4.4版本驾讲,且用到了NoopHostnameVerifier這個(gè)類,而maven仲裁時(shí)選擇了4.6席赂,則會(huì)導(dǎo)致ClassNotFoundException異常吮铭。

  • java.lang.NoSuchMethodError,即找不到特定方法颅停,第一類沖突和第二類沖突都可能導(dǎo)致該問題——加載的類不正確谓晌。若是第一類沖突,則是由于錯(cuò)誤版本的Jar包與所需要版本的Jar包中的類接口不一致導(dǎo)致癞揉,例如antlr-2.7.2.jar升級(jí)到antlr-2.7.6.Jar時(shí)纸肉,接口antlr.collections.AST.getLine()發(fā)生變動(dòng),當(dāng)maven仲裁選擇了錯(cuò)誤版本而加載了錯(cuò)誤版本的類AST喊熟,則會(huì)導(dǎo)致該異常柏肪;若是第二類沖突,則是由于不同Jar包含有的同名類接口不一致導(dǎo)致芥牌,典型的案例:Apache的commons-lang包烦味,2.x升級(jí)到3.x時(shí),包名直接從commons-lang改為commons-lang3胳泉,部分接口也有所改動(dòng)拐叉,由于包名不同和傳遞性依賴岩遗,經(jīng)常會(huì)出現(xiàn)兩種Jar包同時(shí)在classpath下扇商,org.apache.commons.lang.StringUtils.isBlank就是其中有差異的接口之一,由于Jar包的加載順序宿礁,導(dǎo)致加載了錯(cuò)誤版本的StringUtils類案铺,就可能出現(xiàn)NoSuchMethodError異常。

  • java.lang.NoClassDefFoundError梆靖,java.lang.LinkageError等控汉,原因和上述雷同,

  • 沒有報(bào)錯(cuò)異常返吻,但應(yīng)用的行為跟預(yù)期不一致姑子。這類問題同樣也是由于運(yùn)行時(shí)加載了錯(cuò)誤版本的類導(dǎo)致,但跟前面不同的是测僵,沖突的類接口都是一致的街佑,但具體實(shí)現(xiàn)邏輯有差異谢翎,當(dāng)我們加載的類版本不是我們需要的實(shí)現(xiàn)邏輯,就會(huì)出現(xiàn)行為跟預(yù)期不一致問題沐旨。這類問題通常發(fā)生在我們自己內(nèi)部實(shí)現(xiàn)的多個(gè)Jar包中森逮,由于包路徑和類名命名不規(guī)范等問題,導(dǎo)致兩個(gè)不同的Jar包出現(xiàn)了接口一致但實(shí)現(xiàn)邏輯又各不相同的同名類磁携,從而引發(fā)此問題褒侧。

解決方案

  • 如果有異常堆棧信息,根據(jù)錯(cuò)誤信息即可定位導(dǎo)致沖突的類名
  • 若步驟1無法定位沖突的類來自哪個(gè)Jar包谊迄,可在應(yīng)用程序啟動(dòng)時(shí)加上JVM參數(shù)-verbose:class或者-XX:+TraceClassLoading闷供,日志里會(huì)打印出每個(gè)類的加載信息,如來自哪個(gè)Jar包
  • 定位了沖突類的Jar包之后统诺,通過mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>查看是哪些地方引入的Jar包的這個(gè)版本
  • 確定Jar包來源之后这吻,如果是第一類Jar包沖突,則可用<excludes>排除不需要的Jar包版本或者在依賴管理<dependencyManagement>中申明版本篙议;若是第二類Jar包沖突唾糯,如果可排除,則用<excludes>排掉不需要的那個(gè)Jar包鬼贱,若不能排移怯,則需考慮Jar包的升級(jí)或換個(gè)別的Jar包。
沖突檢測插件

對(duì)于第二類Jar包沖突問題这难,前面也提到過舟误,其核心在于同名類出現(xiàn)在了多個(gè)不同的Jar包中,如果人工來排查該問題姻乓,則需要逐個(gè)點(diǎn)開每個(gè)Jar包嵌溢,然后相互對(duì)比看有沒同名的類夸政,那得多么浪費(fèi)精力袄焯摺?乡翅!好在這種費(fèi)時(shí)費(fèi)力的體力活能交給程序去干剪个。maven-enforcer-plugin秧骑,這個(gè)強(qiáng)大的maven插件,配合extra-enforcer-rules工具扣囊,能自動(dòng)掃描Jar包將沖突檢測并打印出來乎折,其原理其實(shí)也比較簡單,通過掃描Jar包中的class侵歇,記錄每個(gè)class對(duì)應(yīng)的Jar包列表骂澄,如果有多個(gè)即是沖突了,故不必深究惕虑,我們只需要關(guān)注如何用它即可坟冲。

在最終需要打包運(yùn)行的應(yīng)用模塊pom中士修,引入maven-enforcer-plugin的依賴,在build階段即可發(fā)現(xiàn)問題樱衷,并解決它棋嘲。比如對(duì)于具有parent pom的多模塊項(xiàng)目,需要將插件依賴聲明在應(yīng)用模塊的pom中矩桂。這里有童鞋可能會(huì)疑問沸移,為什么不把插件依賴聲明在parent pom中呢?那樣依賴它的應(yīng)用子模塊豈不是都能復(fù)用了侄榴?這里之所以強(qiáng)調(diào)“打包運(yùn)行的應(yīng)用模塊pom”雹锣,是因?yàn)闆_突檢測針對(duì)的是最終集成的應(yīng)用,關(guān)注的是應(yīng)用運(yùn)行時(shí)是否會(huì)出現(xiàn)沖突問題癞蚕,而每個(gè)不同的應(yīng)用模塊蕊爵,各自依賴的Jar包集合是不同的,由此而產(chǎn)生的<ignoreClasses>列表也是有差異的桦山,因此只能針對(duì)應(yīng)用模塊pom分別引入該插件攒射。

...
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>1.4.1</version>
  <executions>
    <execution>
      <id>enforce</id>
      <configuration>
        <rules>
          <dependencyConvergence/>
        </rules>
      </configuration>
      <goals>
        <goal>enforce</goal>
      </goals>
    </execution>
    <execution>
      <id>enforce-ban-duplicate-classes</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <banDuplicateClasses>
            <ignoreClasses>
              <ignoreClass>javax.*</ignoreClass>
              <ignoreClass>org.junit.*</ignoreClass>
              <ignoreClass>net.sf.cglib.*</ignoreClass>
              <ignoreClass>org.apache.commons.logging.*</ignoreClass>
              <ignoreClass>org.springframework.remoting.rmi.RmiInvocationHandler</ignoreClass>
            </ignoreClasses>
            <findAllDuplicates>true</findAllDuplicates>
          </banDuplicateClasses>
        </rules>
        <fail>true</fail>
      </configuration>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>extra-enforcer-rules</artifactId>
      <version>1.0-beta-6</version>
    </dependency>
  </dependencies>
</plugin>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恒水,隨后出現(xiàn)的幾起案子会放,更是在濱河造成了極大的恐慌,老刑警劉巖钉凌,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咧最,死亡現(xiàn)場離奇詭異,居然都是意外死亡御雕,警方通過查閱死者的電腦和手機(jī)矢沿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酸纲,“玉大人捣鲸,你說我怎么就攤上這事「G啵” “怎么了摄狱?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長无午。 經(jīng)常有香客問我,道長祝谚,這世上最難降的妖魔是什么宪迟? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮交惯,結(jié)果婚禮上次泽,老公的妹妹穿的比我還像新娘穿仪。我一直安慰自己,他們只是感情好意荤,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布啊片。 她就那樣靜靜地躺著,像睡著了一般玖像。 火紅的嫁衣襯著肌膚如雪紫谷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天捐寥,我揣著相機(jī)與錄音笤昨,去河邊找鬼。 笑死握恳,一個(gè)胖子當(dāng)著我的面吹牛瞒窒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乡洼,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼崇裁,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了束昵?” 一聲冷哼從身側(cè)響起寇壳,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妻怎,沒想到半個(gè)月后壳炎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逼侦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年匿辩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榛丢。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铲球,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晰赞,到底是詐尸還是另有隱情稼病,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布掖鱼,位于F島的核電站然走,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏戏挡。R本人自食惡果不足惜芍瑞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望褐墅。 院中可真熱鬧拆檬,春花似錦洪己、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至屑那,卻和暖如春拱镐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背齐莲。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國打工痢站, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人选酗。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓阵难,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芒填。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呜叫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

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