P7程序員教你用520的機會搞懂MyBatis中設(shè)計模式源碼——代理模式

一筹吐、引入今天的主題

今天準備寫代理模式的時候猛频,苦思要找什么例子狮崩,剛好今天520就搜了下世界名牌口紅的企業(yè)——YSL(圣羅蘭),就問下了女朋友鹿寻,知道這個嘛睦柴。上圖的回答,簡直讓我懷疑找了個假女朋友()毡熏。

搜YSL也是看見微商在朋友圈發(fā)的廣告坦敌,不知道大家有沒有發(fā)現(xiàn),微商簡直就是代理模式的完美例子痢法,畫個問號狱窘?,向下看

二财搁、正文開始——代理模式

是什么

代理模式是給某一個對象提供一個代理對象蘸炸,并且代理對象持有原對象的引用

在不更改原對象源碼的情況下對原對象的方法進行修改和加強,符合開閉原則

屬于對象的結(jié)構(gòu)型模式

看不太懂妇拯?幻馁,沒有關(guān)系,下面講例子

舉上面的例子——微商

原對象(真實對象):YSL官方商店越锈,買YSL的產(chǎn)品(原對象方法)

代理對象:微商仗嗦,代理YSL官方商店買YSL的產(chǎn)品(原對象方法),為了提高競爭力甘凭,并送一些小禮物(方法修改和加強)

三稀拐、代理模式分類

靜態(tài)代理:指在編譯階段,代理類由程序員寫好丹弱,在程序運行時直接獲取代理對象的源碼進行編譯

動態(tài)代理:編譯階段程序員不寫代理類德撬,而是在程序運行時铲咨,根據(jù)用戶定義的增加規(guī)則來動態(tài)生成原對象的代理對象,(不用想蜓洪,肯定用到了多態(tài))

動態(tài)代理分為面向接口的jdk動態(tài)代理和Cglib動態(tài)代理(暫不做討論纤勒,Mybatis中使用的是jdk動態(tài)代理)。

3.1靜態(tài)代理

3.1.1實現(xiàn)靜態(tài)代理兩個要求

1.原對象和代理對象實現(xiàn)同一個接口

2.代理對象持有原對象的引用隆檀,并在方法中對原對象的方法進行增強

如:

原對象:YSL的官方商店

代理對象:微商摇天,持有YSL的官方商店的引用

實現(xiàn)同一個接口:賣產(chǎn)品

3.1.2代碼實現(xiàn)

/**

* @Author Think-Coder

* @Data 2020/5/14 10:55

* @Version 1.0

*/

//定義一個賣化妝品的接口

public interface MakeUpSeller {

//銷售的方法

//name為化妝品名字,price是價格

void sell(String name,double price);

}

//原對象—————YSL官方商店

public class YSLSeller implements MakeUpSeller {

@Override

public void sell(String name, double price) {

System.out.println("感謝購買"+name+",一共是"+price+"元");

}

}

//代理對象————微商代理YSL官方商店

public class WeiShangProxy implements MakeUpSeller {

//持有YSL官方商店的引用

private YSLSeller yslSeller;

public WeiShangProxy(YSLSeller yslSeller) {

this.yslSeller = yslSeller;

}

//實現(xiàn)接口的sell方法恐仑,并增強原對象YSL官方商店的方法

//增強原對象的方法:兩個輸出方法

@Override

public void sell(String name, double price) {

System.out.println("我要發(fā)朋友圈,介紹商品優(yōu)勢");

//YSL官方商店對象調(diào)用賣產(chǎn)品的接口

yslSeller.sell(name,price);

System.out.println("并送您一瓶卸妝水泉坐,歡迎下次再來");

}

}

測試類ProxyTest

public class ProxyTest {

public static void main(String[] args) {

//將new的YSLSeller官方商店原對象傳入微商代理對象

//微商代理對象實現(xiàn)了客戶對YSL官方商店的訪問控制

WeiShangProxy weiShangProxy = new WeiShangProxy(new YSLSeller());

//微商代理對象調(diào)用賣產(chǎn)品方法

weiShangProxy.sell("YSL口紅",1000);

}

}

看下面的結(jié)果是不是很暖心

我要發(fā)朋友圈,介紹商品優(yōu)勢

感謝購買YSL口紅,一共是1000.0元

并送您一瓶卸妝水,歡迎下次再來

Process finished with exit code 0

用類圖做個總結(jié):

在測試類中最重要的就是將new YSLSeller()對象放入WeiShangProxy構(gòu)造函數(shù)中

也就是說客戶直接訪問了微商代理類裳仆,從而微商代理控制了客戶對YSL官方商店的訪問

靜態(tài)代理缺點:

靜態(tài)代理是面向?qū)崿F(xiàn)編程(YSLSeller實現(xiàn)了MakeUpSeller接口)而不是面向接口編程腕让,就把程序?qū)懰懒耍焕诔绦虻臄U展歧斟,即如果原對象增加或刪除方法纯丸,代理對象也會跟著改變,極大提高代碼維護成本

于是就有了JDK動態(tài)代理

3.2jdk動態(tài)代理

3.2.1定義

在程序運行時构捡,根據(jù)用戶的定義規(guī)則液南,動態(tài)生成原對象的代理對象,

用上邊的例子解釋就是,不寫微商代理類勾徽,而是在程序運行時利用Proxy類及InvocationHandler接口等動態(tài)生成代理類及代理實例滑凉。

3.2.2jdk動態(tài)代理的兩個核心方法

Proxy類的newProxyInstance方法:生成原對象的代理對象

InvocationHandler接口的invoke方法:包裝原對象的方法,并增強

Proxy類的newProxyInstance方法

生成代理對象

/**

* 參數(shù)1:ClassLoader loader,原對象的類加載器

* 參數(shù)2:Class[] interfaces,原對象繼承(實現(xiàn))的類和接口Class類數(shù)組

* 參數(shù)3:InvocationHandler h,用戶自定義增強原對象的方法接口

**/

public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)

//上面省略

/*

* Look up or generate the designated proxy class.

* 查找或生成指定的代理類

*/

Class cl = getProxyClass0(loader, intfs);

//下面省略

}

InvocationHandler接口的invoke方法

用戶自定義的規(guī)則接口需要實現(xiàn)此接口喘帚,invoke方法用于增加原代理對象方法

public interface InvocationHandler {

/**

* 參數(shù)1:Object proxy,代理對象

* 參數(shù)2:Method method,原對象方法對應(yīng)的反射類畅姊,method.invoke反射調(diào)用原對象方法

* 參數(shù)3:Object[] args,傳入方法參數(shù)

**/

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable;

}

3.2.3拿上面的例子舉例

微商代理類已經(jīng)不需要了,可以動態(tài)生成

MakeUpSeller接口及YSLSeller官方商店類不發(fā)生變化

加入MakeUpSellerHandler類實現(xiàn)InvocationHandler接口吹由,用于增強原對象方法

完整代碼如下

/**

* @Author Think-Coder

* @Data 2020/5/14 10:55

* @Version 1.0

*/

//定義一個賣化妝品的接口

public interface MakeUpSeller {

//銷售的方法

//name為化妝品名字若未,price是價格

void sell(String name,double price);

}

//原對象—————YSL官方商店

public class YSLSeller implements MakeUpSeller {

@Override

public void sell(String name, double price) {

System.out.println("感謝購買"+name+",一共是"+price+"元");

}

}

//實現(xiàn)InvocationHandler接口

public class MakeUpSellerHandler implements InvocationHandler {

//持有原對象的父類的引用,父類引用指向子類對象倾鲫,多態(tài)的體現(xiàn)

private MakeUpSeller makeUpSeller;

public MakeUpSellerHandler(MakeUpSeller makeUpSeller) {

this.makeUpSeller = makeUpSeller;

}

@Override

//增強原對象的方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("我要發(fā)朋友圈,介紹商品優(yōu)勢");

//反射調(diào)用原對象的方法

method.invoke(makeUpSeller,args);

System.out.println("并送您一瓶卸妝水粗合,歡迎下次再來");

return null;

}

}

看下測試類

public class ProxyTest {

public static void main(String[] args) {

/**

* 參數(shù)1:MakeUpSeller.class.getClassLoader(),MakeUpSeller的類加載器

* 參數(shù)2:new Class[]{MakeUpSeller.class},MakeUpSeller繼承(實現(xiàn))的類和接口Class數(shù)組

* 參數(shù)3:new MakeUpSellerHandler(new YSLSeller()),用戶自定義增強原對象的方法接口

**/

MakeUpSeller yslProxy = (MakeUpSeller) Proxy.newProxyInstance(MakeUpSeller.class.getClassLoader(),

new Class[]{MakeUpSeller.class},

new MakeUpSellerHandler(new YSLSeller()));

yslProxy.sell("YSL口紅",1000);

}

}

看測試結(jié)果

我要發(fā)朋友圈,介紹商品優(yōu)勢

感謝購買YSL口紅,一共是1000.0元

并送您一瓶卸妝水,歡迎下次再來

Process finished with exit code 0

至此動態(tài)代理就實現(xiàn)了

不過乌昔,還有兩個疑問沒有解決

1.為什么Proxy.newProxyInstance方法生成的代理對象可以強轉(zhuǎn)成MakeUpSeller接口類型隙疚?

2.為什么代理對象調(diào)用sell方法,會調(diào)用MakeUpSellerHandler的invoke方法磕道?

帶著這兩個疑問供屉,咱們反編譯下生成動態(tài)代理類

編譯是.java文件編譯為.class文件,反編譯為.class文件變?yōu)?java文件的過程

反編譯生成動態(tài)代理類

改下測試類代碼

public static void main(String[] args) throws IOException {

MakeUpSeller yslProxy = (MakeUpSeller) Proxy.newProxyInstance(MakeUpSeller.class.getClassLoader(),new Class[]{MakeUpSeller.class},

new MakeUpSellerHandler(new YSLSeller()));

yslProxy.sell("YSL口紅",1000);

createProxyClass();

}

public static void createProxyClass() throws IOException {

byte[] bytes = ProxyGenerator.generateProxyClass("MakeUpSeller$proxy", new Class[]{MakeUpSeller.class});

Files.write(new File("D:\\ITProject\\javaproj\\selfproj\\ProxyTest\\out\\production\\ProxyTest\\MakeUpSeller$proxy.class").toPath(),bytes);

}

生成的文件如下

代碼如下,做了部分省略伶丐,

//繼承Proxy代理類悼做,實現(xiàn)了MakeUpSeller接口

//這個就可以回答第一個問題,可以轉(zhuǎn)成MakeUpSeller類型

public final class MakeUpSeller$proxy extends Proxy implements MakeUpSeller {

private static Method m1;

private static Method m2;

private static Method m3;

private static Method m0;

public MakeUpSeller$proxy(InvocationHandler var1) throws? {

super(var1);

}

//實現(xiàn)MakeUpSeller接口sell類

public final void sell(String var1, double var2) throws? {

try {

//這行代碼很重要哗魂,回答了第二個問題

//該類繼承proxy類肛走,h便為InvocationHandler接口,因此可以調(diào)用invoke方法

//而MakeUpSellerHandler實現(xiàn)了InvocationHandler接口录别,因此直接調(diào)用了

//MakeUpSellerHandler類中invoke方法

super.h.invoke(this, m3, new Object[]{var1, var2});

} catch (RuntimeException | Error var5) {

throw var5;

} catch (Throwable var6) {

throw new UndeclaredThrowableException(var6);

}

}

}

如此就可以解釋上面的兩個問題了

最后也用類圖總結(jié)一下

main方法用代理對象調(diào)用sell方法時羹与,其實是動態(tài)生成的MakeUpSeller$proxy類實例調(diào)用的sell方法

根據(jù)上面反編譯類中sell方法中,調(diào)用的是MakeUpSellerHandler接口中invoke方法庶灿,invoke方法中包裝了原對象YSLSeller的sell方法,最后實現(xiàn)了動態(tài)代理吃衅。

接下來看jdk動態(tài)代理在Mybatis中的應(yīng)用往踢,終于到了

四、動態(tài)代理在MyBatis中的應(yīng)用

4.1手寫的MyBtatis框架的測試類

public static void main(String[] args) throws IOException {

//1.讀取配置文件徘层,連接數(shù)據(jù)庫

InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

//2.創(chuàng)建SqlSessionFactory工廠

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

SqlSessionFactory factory = builder.build(in);

//3.使用工廠生產(chǎn)SqlSession對象,用于操作數(shù)據(jù)庫

SqlSession session = factory.openSession();

//4.使用SqlSession創(chuàng)建Dao接口的代理對象峻呕,因為IUserDao接口沒有實現(xiàn)類

IUserDao userDao = session.getMapper(IUserDao.class);

//5.使用代理對象執(zhí)行方法

List users = userDao.findAll();

for (User user:users){

System.out.println(user);

}

//6.釋放資源

session.close();

in.close();

}

在短短的測試類中就使用了三個設(shè)計模式,確實對初學(xué)者不太友好趣效,所以一點一點拆開來看未免不是一個好的學(xué)習(xí)習(xí)慣瘦癌,所以今天主要看兩行代碼

//4.使用SqlSession創(chuàng)建Dao接口的代理對象,因為IUserDao接口沒有實現(xiàn)類

IUserDao userDao = session.getMapper(IUserDao.class);

//5.使用代理對象執(zhí)行方法

List users = userDao.findAll();

看完上面的動態(tài)代理跷敬,再看這兩行代碼就能解開初學(xué)Mybatis時候的疑惑讯私,

為什么只有Dao層接口,沒有Dao層的接口實現(xiàn)類就可以操作數(shù)據(jù)庫西傀?

就是用到了jdk的動態(tài)代理生成了Dao層接口的代理對象userDao

下面從源碼分析一下斤寇,Mybatis底層是怎么創(chuàng)建Dao層接口的代理對象的

4.2MapperProxyFactory類創(chuàng)建Dao層接口代理對象

也就是研究下面的代碼

IUserDao userDao = session.getMapper(IUserDao.class);

當調(diào)用幾個類的getMapper方法后,會調(diào)用下面類第1個newInstance方法

public class MapperProxyFactory {

private final Class mapperInterface;

private final Map methodCache = new ConcurrentHashMap();

//通過構(gòu)造函數(shù)傳入IUerDao接口Class對象

//學(xué)過反射的童鞋應(yīng)該知道拥褂,拿到Class對象娘锁,相當于拿到IUserDao類

public MapperProxyFactory(Class mapperInterface) {

this.mapperInterface = mapperInterface;

}

public Class getMapperInterface() {

return this.mapperInterface;

}

public Map getMethodCache() {

return this.methodCache;

}

//先調(diào)用此方法

public T newInstance(SqlSession sqlSession) {

MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);

//調(diào)用下面newInstance方法

return this.newInstance(mapperProxy);

}

protected T newInstance(MapperProxy mapperProxy) {

/**

* 有沒有很熟悉!

* mapperInterface就是Dao層接口? IUserDao

* 參數(shù)1:this.mapperInterface.getClassLoader(),IUserDao的類加載器

* 參數(shù)2:new Class[]{this.mapperInterface},IUserDao繼承(實現(xiàn))的類和接口Class數(shù)組

* 參數(shù)3:mapperProxy,上邊的newInstace方法返回的饺鹃,實現(xiàn)了InvocationHandler接口莫秆,用于方法增強

**/

return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);

}

}

上面的代碼,寫注釋的地方是重點

MapperProxyFactory類就是創(chuàng)建代理對象的工廠類悔详,自定義Dao層接口傳入構(gòu)造函數(shù)镊屎,通過newInstance方法返回自定義Dao層接口的代理對象

4.3使用代理對象執(zhí)行findAll方法

List<User> users = userDao.findAll();

看到代碼不得不提出兩個問題

1.代理對象userDao是如何執(zhí)行findAll()方法的

2.findAll方法是如何找到對應(yīng)的sql語句進行增刪改查的

4.3.1首先看MapperProxy類

該類實現(xiàn)InvocationHandler接口,重寫的invoke方法包裝了原對象IUserDao接口中findAll方法

也就是說伟端,當執(zhí)行userDao.findAll();時杯道,會調(diào)用該類的invoke方法

invoke方法作用:生成findAll方法對應(yīng)的MapperMethod類實例,MapperMethod類是最重要的,在下面

public class MapperProxy implements InvocationHandler, Serializable {

private static final Method privateLookupInMethod;

private final SqlSession sqlSession;

private final Class mapperInterface;

private final Map methodCache;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//上面省略

//下面兩行代碼很重要

//method為Dao層自定義接口方法

//調(diào)用下面的cachedMapperMethod找到與要執(zhí)行的Dao層接口方法對應(yīng)的MapperMethod

MapperMethod mapperMethod = this.cachedMapperMethod(method);

//調(diào)用execute方法來執(zhí)行findAll方法

//先把sqlSession傳入到MapperMethod內(nèi)部

//在MapperMethod內(nèi)部將要執(zhí)行的方法名和參數(shù)再傳入sqlSession對應(yīng)方法中去執(zhí)行

return mapperMethod.execute(this.sqlSession, args);

}

//根據(jù)的傳入IUserDao接口自定義方法findAll党巾,生成對應(yīng)的MapperMethod類實例

private MapperMethod cachedMapperMethod(Method method) {

return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {

return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());

});

}

}

4.3.2再看MapperMethod類

該類的兩個作用

1.解析接口自定義的findAll方法

2.并找到執(zhí)行對應(yīng)的sql語句的方法

先看是如何解析的

public class MapperMethod {

//SqlCommand內(nèi)部類解析自定義接口方法的方法名稱和SQL語句類型萎庭,

private final MapperMethod.SqlCommand command;

//MethodSignature內(nèi)部類解析接口方法的簽名,即接口方法和參數(shù)名稱和參數(shù)值映射關(guān)系齿拂,如String a="0"

private final MapperMethod.MethodSignature method;

public MapperMethod(Class mapperInterface, Method method, Configuration config) {

this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);

this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);

}

}

那么問題來了驳规,該類是如何找到findAll方法對應(yīng)的sql語句呢?

答案就是Configuration對象署海,通過MapperMethod構(gòu)造函數(shù)傳進來的

如圖所示Configuration中的mapperedStatements字段中的MapperedStatement對象是一個Map類型

key為findAll方法吗购,value中包含sql語句,可以通過方法名findAll找到對應(yīng)的sql語句(這個就是上面第二個問題的答案)

再看execute方法為findAll方法找到的sql語句類型匹配方法

execute方法源碼

public Object execute(SqlSession sqlSession, Object[] args) {

Object result;

Object param;

//根據(jù)SqlCommand解析出來的sql語句類型砸狞,為增刪改查類型匹配方法

switch(this.command.getType()) {

case INSERT:

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));

break;

case UPDATE:

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.update(this.command.getName(), param));

break;

case DELETE:

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));

break;

case SELECT:

if (this.method.returnsVoid() && this.method.hasResultHandler()) {

this.executeWithResultHandler(sqlSession, args);

result = null;

} else if (this.method.returnsMany()) {

result = this.executeForMany(sqlSession, args);

} else if (this.method.returnsMap()) {

result = this.executeForMap(sqlSession, args);

} else if (this.method.returnsCursor()) {

result = this.executeForCursor(sqlSession, args);

} else {

param = this.method.convertArgsToSqlCommandParam(args);

result = sqlSession.selectOne(this.command.getName(), param);

if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {

result = Optional.ofNullable(result);

}

}

break;

case FLUSH:

result = sqlSession.flushStatements();

break;

default:

throw new BindingException("Unknown execution method for: " + this.command.getName());

}

if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {

throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");

} else {

return result;

}

}

根據(jù)sql語句類型匹配對應(yīng)的方法后捻勉,其實是調(diào)用SqlSession接口的實現(xiàn)類執(zhí)行sql語句

如根據(jù)查找到executeForMany方法

private Object executeForMany(SqlSession sqlSession, Object[] args) {

Object param = this.method.convertArgsToSqlCommandParam(args);

List result;

if (this.method.hasRowBounds()) {

RowBounds rowBounds = this.method.extractRowBounds(args);

//最后執(zhí)行sqlSession接口中的selectList方法

result = sqlSession.selectList(this.command.getName(), param, rowBounds);

} else {

result = sqlSession.selectList(this.command.getName(), param);

}

if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {

return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);

} else {

return result;

}

}

SqlSession接口

public interface SqlSession extends Closeable {

/**

* var1:Dao層自定義接口的方法名稱,即findAll()

* var2:方法的參數(shù)

* var3:用于分頁查詢

**/

T selectOne(String var1);

T selectOne(String var1, Object var2);

List selectList(String var1, Object var2, RowBounds var3);

....

}

最后交給SqlSession實現(xiàn)類DefaultSqlSession去執(zhí)行findAll方法對應(yīng)sql語句,并返回結(jié)果

這個和我們直接用SqlSession對象調(diào)用DefaultSqlSession的實現(xiàn)類的方法是一樣的刀森,轉(zhuǎn)了一圈回來踱启,就完成了動態(tài)代理

五、圖總結(jié)

當代理對象userDao調(diào)用findAll()執(zhí)行的代碼流程

原文鏈接:https://blog.csdn.net/shang_0122/article/details/106105717

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末研底,一起剝皮案震驚了整個濱河市埠偿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榜晦,老刑警劉巖冠蒋,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乾胶,居然都是意外死亡抖剿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門识窿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牙躺,“玉大人,你說我怎么就攤上這事腕扶∧蹩剑” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵半抱,是天一觀的道長脓恕。 經(jīng)常有香客問我,道長窿侈,這世上最難降的妖魔是什么炼幔? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮史简,結(jié)果婚禮上乃秀,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好跺讯,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布枢贿。 她就那樣靜靜地躺著,像睡著了一般刀脏。 火紅的嫁衣襯著肌膚如雪局荚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天愈污,我揣著相機與錄音耀态,去河邊找鬼。 笑死暂雹,一個胖子當著我的面吹牛首装,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杭跪,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼簿盅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了揍魂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棚瘟,失蹤者是張志新(化名)和其女友劉穎现斋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偎蘸,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡庄蹋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了迷雪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片限书。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖章咧,靈堂內(nèi)的尸體忽然破棺而出倦西,到底是詐尸還是另有隱情,我是刑警寧澤赁严,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布扰柠,位于F島的核電站,受9級特大地震影響疼约,放射性物質(zhì)發(fā)生泄漏卤档。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一程剥、第九天 我趴在偏房一處隱蔽的房頂上張望劝枣。 院中可真熱鬧,春花似錦、人聲如沸舔腾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琢唾。三九已至载荔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間采桃,已是汗流浹背懒熙。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留普办,地道東北人工扎。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像衔蹲,于是被迫代替她去往敵國和親肢娘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355