MyBatis源碼最后一天

一、MyBatis插件原理

1.MyBatis插件介紹

MyBatis提供了一種插件(plugin)的功能厚宰,雖然叫做插件,但其實(shí)這是攔截器功能。那么攔截器攔截MyBatis中的哪些內(nèi)容呢撬讽?

我們進(jìn)入官網(wǎng)看一看:

MyBatis 允許你在已映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下悬垃,MyBatis 允許使用插件來攔截的方法調(diào)用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

我們看到了可以攔截Executor接口的部分方法游昼,比如update,query尝蠕,commit烘豌,rollback等方法,還有其他接口的一些方法等契耿。

總體概括為:

1)攔截執(zhí)行器的方法

2)攔截參數(shù)的處理

3)攔截結(jié)果集的處理

4)攔截Sql語法構(gòu)建的處理

2.攔截器介紹及配置

首先我們看下MyBatis攔截器的接口定義:

比較簡(jiǎn)單卧斟,只有3個(gè)方法膝宁。 MyBatis默認(rèn)沒有一個(gè)攔截器接口的實(shí)現(xiàn)類,開發(fā)者們可以實(shí)現(xiàn)符合自己需求的攔截器标锄。

下面的MyBatis官網(wǎng)的一個(gè)攔截器實(shí)例:

全局xml配置:

這個(gè)攔截器攔截Executor接口的update方法(其實(shí)也就是SqlSession的新增,刪除茁计,修改操作)料皇,所有執(zhí)行executor的update方法都會(huì)被該攔截器攔截到。

3.源碼分析

下面我們分析一下這段代碼背后的源碼簸淀。

首先從源頭->配置文件開始分析瓶蝴,XMLConfigBuilder解析MyBatis全局配置文件的pluginElement私有方法:

具體的解析代碼其實(shí)比較簡(jiǎn)單,就不貼了租幕,主要就是通過反射實(shí)例化plugin節(jié)點(diǎn)中的interceptor屬性表示的類舷手。然后調(diào)用全局配置類Configuration的addInterceptor方法。

這個(gè)interceptorChain是Configuration的內(nèi)部屬性劲绪,類型為InterceptorChain男窟,也就是一個(gè)攔截器鏈,我們來看下它的定義:

現(xiàn)在我們理解了攔截器配置的解析以及攔截器的歸屬贾富,現(xiàn)在我們回過頭看下為何攔截器會(huì)攔截這些方法(Executor歉眷,ParameterHandler,ResultSetHandler颤枪,StatementHandler的部分方法):

以上4個(gè)方法都是Configuration的方法汗捡。這些方法在MyBatis的一個(gè)操作(新增,刪除,修改扇住,查詢)中都會(huì)被執(zhí)行到春缕,執(zhí)行的先后順序是Executor,ParameterHandler艘蹋,ResultSetHandler锄贼,StatementHandler(其中ParameterHandler和ResultSetHandler的創(chuàng)建是在創(chuàng)建StatementHandler[3個(gè)可用的實(shí)現(xiàn)類CallableStatementHandler,PreparedStatementHandler,SimpleStatementHandler]的時(shí)候,其構(gòu)造函數(shù)調(diào)用的[這3個(gè)實(shí)現(xiàn)類的構(gòu)造函數(shù)其實(shí)都調(diào)用了父類BaseStatementHandler的構(gòu)造函數(shù)])女阀。

這4個(gè)方法實(shí)例化了對(duì)應(yīng)的對(duì)象之后宅荤,都會(huì)調(diào)用interceptorChain的pluginAll方法,InterceptorChain的pluginAll剛才已經(jīng)介紹過了浸策,就是遍歷所有的攔截器冯键,然后調(diào)用各個(gè)攔截器的plugin方法。注意:攔截器的plugin方法的返回值會(huì)直接被賦值給原先的對(duì)象庸汗。

由于可以攔截StatementHandler琼了,這個(gè)接口主要處理sql語法的構(gòu)建,因此比如分頁的功能夫晌,可以用攔截器實(shí)現(xiàn)雕薪,只需要在攔截器的plugin方法中處理StatementHandler接口實(shí)現(xiàn)類中的sql即可,可使用反射實(shí)現(xiàn)晓淀。

MyBatis還提供了 @Intercepts和 @Signature關(guān)于攔截器的注解所袁。官網(wǎng)的例子就是使用了這2個(gè)注解,還包括了Plugin類的使用:

下面我們就分析這3個(gè) "新組合" 的源碼凶掰,首先先看Plugin類的wrap方法:

Plugin類實(shí)現(xiàn)了InvocationHandler接口燥爷,很明顯,我們看到這里返回了一個(gè)JDK自身提供的動(dòng)態(tài)代理類懦窘。我們解剖一下這個(gè)方法調(diào)用的其他方法前翎,getSignatureMap方法:

getSignatureMap方法解釋:首先會(huì)拿到攔截器這個(gè)類的 @Interceptors注解,然后拿到這個(gè)注解的屬性 @Signature注解集合畅涂,然后遍歷這個(gè)集合港华,遍歷的時(shí)候拿出 @Signature注解的type屬性(Class類型),然后根據(jù)這個(gè)type得到帶有method屬性和args屬性的Method午衰。由于 @Interceptors注解的 @Signature屬性是一個(gè)屬性立宜,所以最終會(huì)返回一個(gè)以type為key,value為Set的Map臊岸。

比如這個(gè) @Interceptors注解會(huì)返回一個(gè)key為Executor橙数,value為集合(這個(gè)集合只有一個(gè)元素,也就是Method實(shí)例帅戒,這個(gè)Method實(shí)例就是Executor接口的update方法灯帮,且這個(gè)方法帶有MappedStatement和Object類型的參數(shù))。這個(gè)Method實(shí)例是根據(jù) @Signature的method和args屬性得到的。如果args參數(shù)跟type類型的method方法對(duì)應(yīng)不上钟哥,那么將會(huì)拋出異常响疚。

getAllInterfaces方法:

getAllInterfaces方法解釋:根據(jù)目標(biāo)實(shí)例target(這個(gè)target就是之前所說的MyBatis攔截器可以攔截的類,Executor,ParameterHandler,ResultSetHandler,StatementHandler)和它的父類們瞪醋,返回signatureMap中含有target實(shí)現(xiàn)的接口數(shù)組。

所以Plugin這個(gè)類的作用就是根據(jù) @Interceptors注解装诡,得到這個(gè)注解的屬性 @Signature數(shù)組银受,然后根據(jù)每個(gè) @Signature注解的type,method鸦采,args屬性使用反射找到對(duì)應(yīng)的Method宾巍。最終根據(jù)調(diào)用的target對(duì)象實(shí)現(xiàn)的接口決定是否返回一個(gè)代理對(duì)象替代原先的target對(duì)象。

比如MyBatis官網(wǎng)的例子渔伯,當(dāng)Configuration調(diào)用newExecutor方法的時(shí)候顶霞,由于Executor接口的update(MappedStatement ms, Object parameter)方法被攔截器被截獲。因此最終返回的是一個(gè)代理類Plugin锣吼,而不是Executor选浑。這樣調(diào)用方法的時(shí)候,如果是個(gè)代理類玄叠,那么會(huì)執(zhí)行:

沒錯(cuò)古徒,如果找到對(duì)應(yīng)的方法被代理之后,那么會(huì)執(zhí)行Interceptor接口的interceptor方法读恃。

這個(gè)Invocation類如下:

它的proceed方法也就是調(diào)用原先方法(不走代理)隧膘。

4.總結(jié)

MyBatis攔截器接口提供的3個(gè)方法中,plugin方法用于某些處理器(Handler)的構(gòu)建過程寺惫。interceptor方法用于處理代理類的執(zhí)行疹吃。setProperties方法用于攔截器屬性的設(shè)置。

其實(shí)MyBatis官網(wǎng)提供的使用 @Interceptors和 @Signature注解以及Plugin類這樣處理攔截器的方法西雀,我們不一定要直接這樣使用萨驶。我們也可以拋棄這3個(gè)類,直接在plugin方法內(nèi)部根據(jù)target實(shí)例的類型做相應(yīng)的操作艇肴。

總體來說MyBatis攔截器還是很簡(jiǎn)單的篡撵,攔截器本身不需要太多的知識(shí)點(diǎn),但是學(xué)習(xí)攔截器需要對(duì)MyBatis中的各個(gè)接口很熟悉豆挽,因?yàn)閿r截器涉及到了各個(gè)接口的知識(shí)點(diǎn)育谬。


二、MyBatis執(zhí)行Sql的流程分析

本章著重介紹MyBatis執(zhí)行Sql的流程帮哈,關(guān)于在執(zhí)行過程中緩存膛檀、動(dòng)態(tài)SQl生成等細(xì)節(jié)不在本章中體現(xiàn)。

1.事例

還是以之前的查詢作為列子:

之前提到拿到sqlSession之后就能進(jìn)行各種CRUD操作了,所以我們就從sqlSession.getMapper這個(gè)方法開始分析咖刃,看下整個(gè)Sql的執(zhí)行流程是怎么樣的泳炉。

2.openSession的過程

Executor分成兩大類,一類是CacheExecutor嚎杨,另一類是普通Executor花鹅。

普通Executor又分為三種基本的Executor執(zhí)行器,SimpleExecutor枫浙、ReuseExecutor刨肃、BatchExecutor。

1)SimpleExecutor:每執(zhí)行一次update或select箩帚,就開啟一個(gè)Statement對(duì)象真友,用完立刻關(guān)閉Statement對(duì)象。

2)ReuseExecutor:執(zhí)行update或select紧帕,以sql作為key查找Statement對(duì)象盔然,存在就使用,不存在就創(chuàng)建是嗜,用完后愈案,不關(guān)閉Statement對(duì)象,而是放置于Map內(nèi)鹅搪,供下一次使用刻帚。簡(jiǎn)言之,就是重復(fù)使用Statement對(duì)象涩嚣。

3)BatchExecutor:執(zhí)行update(沒有select崇众,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch())航厚,等待統(tǒng)一執(zhí)行(executeBatch())顷歌,它緩存了多個(gè)Statement對(duì)象,每個(gè)Statement對(duì)象都是addBatch()完畢后幔睬,等待逐一執(zhí)行executeBatch()批處理眯漩。與JDBC批處理相同。

作用范圍:Executor的這些特點(diǎn)麻顶,都嚴(yán)格限制在SqlSession生命周期范圍內(nèi)赦抖。

CacheExecutor其實(shí)是封裝了普通的Executor,和普通的區(qū)別是在查詢前先會(huì)查詢緩存中是否存在結(jié)果辅肾,如果存在就使用緩存中的結(jié)果队萤,如果不存在還是使用普通的Executor進(jìn)行查詢,再將查詢出來的結(jié)果存入緩存矫钓。

到此為止要尔,我們已經(jīng)獲得了SqlSession舍杜,拿到SqlSession就可以執(zhí)行各種CRUD方法了。

3.總結(jié)

3.1簡(jiǎn)單總結(jié)

1)拿到SqlSessionFactory對(duì)象后赵辕,會(huì)調(diào)用SqlSessionFactory的openSesison方法既绩,這個(gè)方法會(huì)創(chuàng)建一個(gè)Sql執(zhí)行器(Executor),這個(gè)Sql執(zhí)行器會(huì)代理你配置的攔截器方法还惠。

2)獲得上面的Sql執(zhí)行器后饲握,會(huì)創(chuàng)建一個(gè)SqlSession(默認(rèn)使用DefaultSqlSession),這個(gè)SqlSession中也包含了Configration對(duì)象,所以通過SqlSession也能拿到全局配置蚕键。

3)獲得SqlSession對(duì)象后就能執(zhí)行各種CRUD方法了救欧。

3.2一些重要類總結(jié)

1)SqlSessionFactory

2)SqlSessionFactoryBuilder

3)SqlSession(默認(rèn)使用DefaultSqlSession)

4)Executor接口

5)Plugin、InterceptorChain的pluginAll方法

4.獲取Mapper的流程

進(jìn)入sqlSession.getMapper方法嚎幸,會(huì)發(fā)現(xiàn)調(diào)的是Configration對(duì)象的getMapper方法:

進(jìn)入getMapper方法:

進(jìn)入MapperProxyFactory的newInstance方法:

獲取Mapper的流程總結(jié)如下:

Mapper方法的執(zhí)行流程:

下面是動(dòng)態(tài)代理類MapperProxy,調(diào)用Mapper接口的所有方法都會(huì)先調(diào)用到這個(gè)代理類的invoke方法(注意由于Mybatis中的Mapper接口沒有實(shí)現(xiàn)類寄猩,所以MapperProxy這個(gè)代理對(duì)象中沒有委托類嫉晶,也就是說MapperProxy干了代理類和委托類的事情)。好了下面重點(diǎn)看下invoke方法田篇。

MapperProxy的invoke方法非常簡(jiǎn)單替废,主要干的工作就是創(chuàng)建MapperMethod對(duì)象或者是從緩存中獲取MapperMethod對(duì)象。獲取到這個(gè)對(duì)象后執(zhí)行execute方法泊柬。

所以這邊需要進(jìn)入MapperMethod的execute方法:這個(gè)方法判斷你當(dāng)前執(zhí)行的方式是增刪改查哪一種椎镣,并通過SqlSession執(zhí)行相應(yīng)的操作。(這邊以sqlSession.selectOne這種方式進(jìn)行分析~)

sqlSession.selectOne方法會(huì)會(huì)調(diào)到DefaultSqlSession的selectList方法兽赁。這個(gè)方法獲取了獲取了MappedStatement對(duì)象状答,并最終調(diào)用了Executor的query方法。

然后刀崖,通過一層一層的調(diào)用(這邊省略了緩存操作的環(huán)節(jié)惊科,會(huì)在后面的文章中介紹),最終會(huì)來到doQuery方法亮钦, 這兒咱們就隨便找個(gè)Excutor看看doQuery方法的實(shí)現(xiàn)吧馆截,我這兒選擇了SimpleExecutor:

接下來,咱們看看StatementHandler 的一個(gè)實(shí)現(xiàn)類 PreparedStatementHandler(這也是我們最常用的蜂莉,封裝的是PreparedStatement), 看看它使怎么去處理的:

到此蜡娶,整個(gè)調(diào)用流程結(jié)束。

5.簡(jiǎn)單總結(jié)

這邊結(jié)合獲取SqlSession的流程映穗,做下簡(jiǎn)單的總結(jié):

1)SqlSessionFactoryBuilder解析配置文件窖张,包括屬性配置、別名配置蚁滋、攔截器配置荤堪、環(huán)境(數(shù)據(jù)源和事務(wù)管理器)合陵、Mapper配置等;解析完這些配置后會(huì)生成一個(gè)Configration對(duì)象澄阳,這個(gè)對(duì)象中包含了MyBatis需要的所有配置拥知,然后會(huì)用這個(gè)Configration對(duì)象創(chuàng)建一個(gè)SqlSessionFactory對(duì)象,這個(gè)對(duì)象中包含了Configration對(duì)象碎赢。

2)拿到SqlSessionFactory對(duì)象后低剔,會(huì)調(diào)用SqlSessionFactory的openSesison方法,這個(gè)方法會(huì)創(chuàng)建一個(gè)Sql執(zhí)行器(Executor組件中包含了Transaction對(duì)象)肮塞,這個(gè)Sql執(zhí)行器會(huì)代理你配置的攔截器方法襟齿。

3)獲得上面的Sql執(zhí)行器后,會(huì)創(chuàng)建一個(gè)SqlSession(默認(rèn)使用DefaultSqlSession),這個(gè)SqlSession中也包含了Configration對(duì)象和上面創(chuàng)建的Executor對(duì)象枕赵,所以通過SqlSession也能拿到全局配置猜欺;

4)獲得SqlSession對(duì)象后就能執(zhí)行各種CRUD方法了。


以上是獲得SqlSession的流程拷窜,下面總結(jié)下本博客中介紹的Sql的執(zhí)行流程:

1)調(diào)用SqlSession的getMapper方法开皿,獲得Mapper接口的動(dòng)態(tài)代理對(duì)象MapperProxy,調(diào)用Mapper接口的所有方法都會(huì)調(diào)用到MapperProxy的invoke方法(動(dòng)態(tài)代理機(jī)制)篮昧。

2)MapperProxy的invoke方法中唯一做的就是創(chuàng)建一個(gè)MapperMethod對(duì)象赋荆,然后調(diào)用這個(gè)對(duì)象的execute方法,sqlSession會(huì)作為execute方法的入?yún)ⅰ?/p>

3)往下懊昨,層層調(diào)下來會(huì)進(jìn)入Executor組件(如果配置插件會(huì)對(duì)Executor進(jìn)行動(dòng)態(tài)代理)的query方法窄潭,這個(gè)方法中會(huì)創(chuàng)建一個(gè)StatementHandler對(duì)象,這個(gè)對(duì)象中同時(shí)會(huì)封裝ParameterHandler和ResultSetHandler對(duì)象酵颁。調(diào)用StatementHandler預(yù)編譯參數(shù)以及設(shè)置參數(shù)值嫉你,使用ParameterHandler來給sql設(shè)置參數(shù)。

4)Executor組件有兩個(gè)直接實(shí)現(xiàn)類躏惋,分別是BaseExecutor和CachingExecutor均抽。CachingExecutor靜態(tài)代理了BaseExecutor。Executor組件封裝了Transction組件其掂,Transction組件中又分裝了Datasource組件油挥。

5)調(diào)用StatementHandler的增刪改查方法獲得結(jié)果,ResultSetHandler對(duì)結(jié)果進(jìn)行封裝轉(zhuǎn)換款熬,請(qǐng)求結(jié)束深寥。

6)Executor、StatementHandler 贤牛、ParameterHandler惋鹅、ResultSetHandler,Mybatis的插件會(huì)對(duì)上面的四個(gè)組件進(jìn)行動(dòng)態(tài)代理殉簸。

6.重要類

MapperRegistry:本質(zhì)上是一個(gè)Map闰集,其中的key是Mapper接口的全限定名沽讹,value是MapperProxyFactory

MapperProxyFactory:這個(gè)類是MapperRegistry中存的value值,在通過sqlSession獲取Mapper時(shí)武鲁,其實(shí)先獲取到的是這個(gè)工廠爽雄,然后通過這個(gè)工廠創(chuàng)建Mapper的動(dòng)態(tài)代理類

MapperProxy:實(shí)現(xiàn)了InvocationHandler接口,Mapper的動(dòng)態(tài)代理接口方法的調(diào)用都會(huì)到達(dá)這個(gè)類的invoke方法

MapperMethod:判斷你當(dāng)前執(zhí)行的方式是增刪改查哪一種沐鼠,并通過SqlSession執(zhí)行相應(yīng)的操作

SqlSession:作為MyBatis工作的主要頂層API挚瘟,表示和數(shù)據(jù)庫交互的會(huì)話,完成必要數(shù)據(jù)庫增刪改查功能

Executor:MyBatis執(zhí)行器饲梭,是MyBatis 調(diào)度的核心乘盖,負(fù)責(zé)SQL語句的生成和查詢緩存的維護(hù)

StatementHandler: 封裝了JDBC Statement操作,負(fù)責(zé)對(duì)JDBC statement 的操作憔涉,如設(shè)置參數(shù)订框、將Statement結(jié)果集轉(zhuǎn)換成List集合

ParameterHandler: 負(fù)責(zé)對(duì)用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所需要的參數(shù)

ResultSetHandler: 負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對(duì)象轉(zhuǎn)換成List類型的集合

TypeHandler: 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換

MappedStatement: MappedStatement維護(hù)了一條節(jié)點(diǎn)的封裝

SqlSource: 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動(dòng)態(tài)地生成SQL語句兜叨,將信息封裝到BoundSql對(duì)象中穿扳,并返回

BoundSql: 表示動(dòng)態(tài)生成的SQL語句以及相應(yīng)的參數(shù)信息

Configuration: MyBatis所有的配置信息都維持在Configuration對(duì)象之中

7.調(diào)試主要關(guān)注點(diǎn)

1)MapperProxy.invoke方法:MyBatis的所有Mapper對(duì)象都是通過動(dòng)態(tài)代理生成的,任何方法的調(diào)用都會(huì)調(diào)到invoke方法浪腐,這個(gè)方法的主要功能就是創(chuàng)建MapperMethod對(duì)象纵揍,并放進(jìn)緩存顿乒。所以調(diào)試時(shí)我們可以在這個(gè)位置打個(gè)斷點(diǎn)议街,看下是否成功拿到了MapperMethod對(duì)象,并執(zhí)行了execute方法璧榄。

2)MapperMethod.execute方法:這個(gè)方法會(huì)判斷你當(dāng)前執(zhí)行的方式是增刪改查哪一種特漩,并通過SqlSession執(zhí)行相應(yīng)的操作。Debug時(shí)也建議在此打個(gè)斷點(diǎn)看下骨杂。

3)DefaultSqlSession.selectList方法:這個(gè)方法獲取了獲取了MappedStatement對(duì)象涂身,并最終調(diào)用了Executor的query方法。

最后編輯于
?著作權(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)離奇詭異雳灾,居然都是意外死亡漠酿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門谎亩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炒嘲,“玉大人宇姚,你說我怎么就攤上這事》蛲梗” “怎么了浑劳?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)寸痢。 經(jīng)常有香客問我呀洲,道長(zhǎng),這世上最難降的妖魔是什么啼止? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任道逗,我火速辦了婚禮,結(jié)果婚禮上献烦,老公的妹妹穿的比我還像新娘滓窍。我一直安慰自己,他們只是感情好巩那,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布吏夯。 她就那樣靜靜地躺著,像睡著了一般即横。 火紅的嫁衣襯著肌膚如雪噪生。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天东囚,我揣著相機(jī)與錄音跺嗽,去河邊找鬼。 笑死页藻,一個(gè)胖子當(dāng)著我的面吹牛桨嫁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播份帐,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼璃吧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了废境?” 一聲冷哼從身側(cè)響起畜挨,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎噩凹,沒想到半個(gè)月后巴元,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一奸鸯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧可帽,春花似錦娄涩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至努隙,卻和暖如春球恤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荸镊。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工咽斧, 沒想到剛下飛機(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)容