代理9 cglib demo分析以及methodProxy與Fastclass源碼

前言

上一節(jié)講了say方法最終會(huì)轉(zhuǎn)發(fā),在demo中
cglib.CglibProxy#intercept這個(gè)里面用了

Object result = methodProxy.invokeSuper(o, objects);

這個(gè)invokeSuper是什么孵淘?如何實(shí)現(xiàn)代理類(lèi)函數(shù)的調(diào)用 轉(zhuǎn)發(fā)到 父類(lèi)對(duì)應(yīng)函數(shù)的調(diào)用.

這里就涉及methodProxy以及FastClass機(jī)制了。
這兩者也是緊密聯(lián)系的。

源碼分析

方法代理的創(chuàng)建 CGLIB$methodName$index$Proxy

先直接講結(jié)論
*** 方法代理的作用就是指明class1.method1方法對(duì)應(yīng)的代理方法是class2.method2 ***

再看代碼
先看cglib.CglibProxy#intercept在前面代理類(lèi)class反編譯文件中傳遞的參數(shù)

Object object = methodInterceptor.intercept((Object)this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);

//CGLIB$say$0$Proxy是什么
CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//其中
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

上面代碼中与殃,MethodProxy#create
就是根據(jù)參數(shù)進(jìn)行"簽名"

  A representation of a method signature, containing the method name,
  return type, and parameter types.

簽名包含方法名稱(chēng),返回類(lèi)型碍现,以及參數(shù)類(lèi)型,看源碼如下

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

*** 這一段的意義就是說(shuō),委托類(lèi)c1的方法name1,對(duì)應(yīng)的代理方法是實(shí)現(xiàn)類(lèi)c2的方法name2***

方法代理的調(diào)用

再看net.sf.cglib.proxy.MethodProxy#invokeSuper干了啥

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

//init函數(shù)干了什么
    private void init()
    {
        /*
        單例模式實(shí)現(xiàn)
         */
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    /*
                    生成fastClass f1,f2,結(jié)合class文件分析
                     */
//                    System.out.println("net.sf.cglib.proxy.MethodProxy.helper " + ci.c1.getName() + "  " + ci.c2.getName() + " " + this.sig1 + " " + this.sig2);
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    /*
                    獲取當(dāng)前方法在f1,f2中的簽名幅疼,得到一個(gè)index(方法與簽名,index是一一對(duì)應(yīng)的關(guān)系)
                    可以根據(jù)index映射對(duì)應(yīng)類(lèi)的對(duì)應(yīng)方法
                     */
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

//helper函數(shù)干了什么
        private static FastClass helper(CreateInfo ci, Class type) {
        //根據(jù)ci得到該方法的委托類(lèi)昼接,實(shí)現(xiàn)類(lèi)爽篷,分別生成這兩個(gè)類(lèi)的fastClass
        FastClass.Generator g = new FastClass.Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }

fastClass創(chuàng)建于調(diào)用

可以直接參考 http://www.cnblogs.com/cruze/p/3865180.html
這里說(shuō)的比較簡(jiǎn)單易懂
主要思想就是在為了避免方法調(diào)用時(shí),過(guò)度使用反射造成調(diào)用慢的問(wèn)題
給每一個(gè)方法一個(gè)簽名慢睡,遇到這個(gè)簽名時(shí)逐工,直接顯示調(diào)用實(shí)現(xiàn)類(lèi)的實(shí)現(xiàn)方法,
可以參考下面的fastClass文件

fastClass的創(chuàng)建

fastClass的創(chuàng)建時(shí)機(jī)

從demo中看膨疏,fastClass是根據(jù)這樣的順序創(chuàng)建的

net.sf.cglib.proxy.MethodProxy#invokeSuper
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create

*** 這里有一種lazy init的思想 ***
也就是說(shuō),一開(kāi)始并沒(méi)有fastClass钻弄,只有MethodProxy
但是當(dāng) 方法代理 真正被請(qǐng)求的時(shí)候
class1的method1方法需要映射到class2的method2方法時(shí),
為了避免過(guò)度使用反射,才生成了fastClass方便的完成調(diào)用
為了方便理解佃却,

fastClass的創(chuàng)建了幾次

即生成了幾個(gè)fastClass類(lèi)的class文件
*** 兩個(gè),f1和f2(理解成是fast(c1)和fast(c2)) ***
net.sf.cglib.proxy.MethodProxy#init里面調(diào)用了兩次net.sf.cglib.proxy.MethodProxy#helper
這兩個(gè)文件的區(qū)別是什么

對(duì)象 意義窘俺,實(shí)例
c1 cglib.CglibLearn$serviceImpl
f1 c1的fastClass,更方便調(diào)用c1的函數(shù) cglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381
c2 cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51 即c1的enhance class
f2 c2的fastClass,更加方便調(diào)用c2的函數(shù), cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902

f1,f2都是對(duì)原有的c1,c2進(jìn)行了method->signature->index的映射

fastClass類(lèi)的內(nèi)容是如何generate出來(lái)的

net.sf.cglib.reflect.FastClass.Generator#generateClass
這里就不細(xì)講了饲帅,和上一節(jié)Enhancer#generateClass干的事情一樣,難度更小瘤泪,底層還是利用asm生成灶泵。

fastClass類(lèi)的內(nèi)容是什么

以f1為例 (f2后面篩選一部分出來(lái)),即
cglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381為例子,class反編譯的代碼如下

package cglib;
import cglib.CglibLearn.serviceImpl;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

/* compiled from: <generated> */
public class CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381 extends FastClass {
    public CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381(Class cls) {
        super(cls);
    }

    public int getIndex(String str, Class[] clsArr) {
        switch (str.hashCode()) {
            case -1776922004:
                if (str.equals("toString")) {
                    switch (clsArr.length) {
                        case 0:
                            return 5;
                        default:
                            break;
                    }
                }
                break;
            case -1295482945:
                if (str.equals("equals")) {
                    switch (clsArr.length) {
                        case 1:
                            if (clsArr[0].getName().equals("java.lang.Object")) {
                                return 4;
                            }
                            break;
                        default:
                            break;
                    }
                }
                break;
            case -1039689911:
                if (str.equals("notify")) {
                    switch (clsArr.length) {
                        case 0:
                            return 8;
                        default:
                            break;
                    }
                }
                break;
            case 113643:
                if (str.equals("say")) {
                    switch (clsArr.length) {
                        case 0:
                            return 0;
                        default:
                            break;
                    }
                }
                break;
            case 3641717:
                if (str.equals("wait")) {
                    switch (clsArr.length) {
                        case 0:
                            return 1;
                        case 1:
                            if (clsArr[0].getName().equals("long")) {
                                return 3;
                            }
                            break;
                        case 2:
                            if (clsArr[0].getName().equals("long") && clsArr[1].getName().equals("int")) {
                                return 2;
                            }
                        default:
                            break;
                    }
                }
                break;
            case 147696667:
                if (str.equals("hashCode")) {
                    switch (clsArr.length) {
                        case 0:
                            return 6;
                        default:
                            break;
                    }
                }
                break;
            case 1902066072:
                if (str.equals("notifyAll")) {
                    switch (clsArr.length) {
                        case 0:
                            return 9;
                        default:
                            break;
                    }
                }
                break;
            case 1950568386:
                if (str.equals("getClass")) {
                    switch (clsArr.length) {
                        case 0:
                            return 7;
                        default:
                            break;
                    }
                }
                break;
        }
        return -1;
    }

    public int getIndex(Signature signature) {
        String obj = signature.toString();
        switch (obj.hashCode()) {
            case -1725733088:
                if (obj.equals("getClass()Ljava/lang/Class;")) {
                    return 7;
                }
                break;
            case -1026001249:
                if (obj.equals("wait(JI)V")) {
                    return 2;
                }
                break;
            case -909388886:
                if (obj.equals("say()V")) {
                    return 0;
                }
                break;
            case 243996900:
                if (obj.equals("wait(J)V")) {
                    return 3;
                }
                break;
            case 946854621:
                if (obj.equals("notifyAll()V")) {
                    return 9;
                }
                break;
            case 1116248544:
                if (obj.equals("wait()V")) {
                    return 1;
                }
                break;
            case 1826985398:
                if (obj.equals("equals(Ljava/lang/Object;)Z")) {
                    return 4;
                }
                break;
            case 1902039948:
                if (obj.equals("notify()V")) {
                    return 8;
                }
                break;
            case 1913648695:
                if (obj.equals("toString()Ljava/lang/String;")) {
                    return 5;
                }
                break;
            case 1984935277:
                if (obj.equals("hashCode()I")) {
                    return 6;
                }
                break;
        }
        return -1;
    }

    public int getIndex(Class[] clsArr) {
        switch (clsArr.length) {
            case 0:
                return 0;
            default:
                return -1;
        }
    }

    public int getMaxIndex() {
        return 9;
    }

    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
            case 1:
                cglib_CglibLearn_serviceImpl.wait();
                return null;
            case 2:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue(), ((Number) objArr[1]).intValue());
                return null;
            case 3:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue());
                return null;
            case 4:
                return new Boolean(cglib_CglibLearn_serviceImpl.equals(objArr[0]));
            case 5:
                return cglib_CglibLearn_serviceImpl.toString();
            case 6:
                return new Integer(cglib_CglibLearn_serviceImpl.hashCode());
            case 7:
                return cglib_CglibLearn_serviceImpl.getClass();
            case 8:
                cglib_CglibLearn_serviceImpl.notify();
                return null;
            case 9:
                cglib_CglibLearn_serviceImpl.notifyAll();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
        invocationTargetException = new InvocationTargetException(th);
    }

    public Object newInstance(int i, Object[] objArr) throws InvocationTargetException {
        switch (i) {
            case 0:
                try {
                    return new serviceImpl();
                } catch (Throwable th) {
                    InvocationTargetException invocationTargetException = new InvocationTargetException(th);
                }
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
}

如何理解上面這段代碼,先看父類(lèi)fastClass的幾個(gè)函數(shù)定義

//根據(jù)方法簽名的hashCode,獲取一個(gè)method的標(biāo)識(shí)index
net.sf.cglib.reflect.FastClass#getIndex(net.sf.cglib.core.Signature)

//根據(jù)method的標(biāo)識(shí)index,調(diào)用實(shí)現(xiàn)類(lèi)的對(duì)應(yīng)方法
net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])

*** 為什么說(shuō)fastClass比反射快 ***
net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])函數(shù)中对途,直接創(chuàng)建了實(shí)現(xiàn)類(lèi)對(duì)象赦邻,通過(guò)標(biāo)識(shí)符index去switch case進(jìn)行對(duì)應(yīng)調(diào)用,就像上面的

    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
//省略

結(jié)果分析

上面invokeSuper返回

fci.f2.invoke(fci.i2, obj, args)

是干了什么
表示用方法代理(否則是f1,這里是f2),將lazy init里面記錄好的方法簽名對(duì)應(yīng)的標(biāo)志i2傳遞過(guò)去实檀,讓f2進(jìn)行對(duì)應(yīng)的處理

上面fastclass是f1的惶洲,這里只貼出來(lái)f2對(duì)應(yīng)部分,f2比較長(zhǎng)

類(lèi)名
cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902

    public int getIndex(Signature signature) {
        String obj = signature.toString();
        switch (obj.hashCode()) {
            case 1540695073:
                if (obj.equals("CGLIB$say$0()V")) {
                    return 17;
                }
                break;
         }
     }

     public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        b2e6ff51 cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51 = (b2e6ff51) obj;
        switch (i) {
            case 17:
                cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }


//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()函數(shù)
    final void CGLIB$say$0() {
        super.say();//即CglibLearn.serviceImpl.say()方法
    }

這里就完成了c1.say到c2.CGLIB$say$0()的轉(zhuǎn)發(fā)
可能會(huì)有疑問(wèn)膳犹,為什么不是c1.say到c2.say的轉(zhuǎn)發(fā)
這是因?yàn)樵赾2里面定義了(上一節(jié)也有這段代碼,本節(jié)最上也有)

CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//其中
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

將Object的say方法 代理 給了 serviceImpl$$EnhancerByCGLIB$$4e65f4b的 CGLIB$say$0方法

即表明恬吕,c1的say方法轉(zhuǎn)發(fā)到了c2的CGLIB$say$0方法

思考

1.fastclass比反射快的原因
通過(guò)方法前面或者標(biāo)識(shí)符index,利用switch case直接利用對(duì)象去調(diào)用函數(shù)
而反射是java.lang.reflect.Method#invoke,稍微復(fù)雜點(diǎn)须床,這個(gè)沒(méi)研究過(guò)具體實(shí)現(xiàn)

2.MethodProxy#invoke和MethodProxy#invokeSuper什么區(qū)別铐料,即[c1,f1]與[c2,f2]的區(qū)別
[c1,f1]對(duì)應(yīng)的是 父類(lèi)的class和fastclass
[c2,f2]對(duì)應(yīng)的是 父類(lèi)的enhanceClass和 enhanceFastClass

3.MethodProxy#init創(chuàng)建fastclass時(shí),每個(gè)method在第一次調(diào)用時(shí),都會(huì)進(jìn)行
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create
那么為什么對(duì)應(yīng)的fastclass文件只生成了一次(不是一個(gè)method調(diào)用一次就生成一次)
并且一次就有整個(gè)類(lèi)的信息豺旬,而不是只有這個(gè)method相關(guān)信息呢

第一點(diǎn):同一個(gè)類(lèi)的fastClass只生成了一次钠惩,

net.sf.cglib.reflect.FastClass.Generator#create
net.sf.cglib.core.AbstractClassGenerator#create
里面用了緩存

第二點(diǎn):一次就有整個(gè)類(lèi)的信息,而不是只有這個(gè)method信息
net.sf.cglib.proxy.MethodProxy#create時(shí)就傳入和class c1,c2
后來(lái)創(chuàng)建fastClass時(shí)
net.sf.cglib.proxy.MethodProxy#helper
調(diào)用了g.setType(type);
在fastClass生成時(shí)
net.sf.cglib.reflect.FastClass.Generator#generateClass
用到了這個(gè)之前設(shè)置好的Class type族阅,也就直到類(lèi)信息了
invokeSuper的邏輯

4.把invokeSuper改成invoke會(huì)怎么樣
結(jié)論:死循環(huán)篓跛,堆棧溢出
原因分析:就相當(dāng)于MethodProxy方法代理中,并沒(méi)有代理
看c1.say方法耘分,上一節(jié)有列舉出來(lái)

final void say() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            methodInterceptor.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
        } else {
            say();
        }
    }

這里會(huì)走到methodInterceptor.intercept,會(huì)進(jìn)過(guò)aop
本來(lái)調(diào)用invokeSuper举塔,在這里如果改成了invoke,那么

    public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (IllegalArgumentException e) {
            if (fastClassInfo.i1 < 0)
                throw new IllegalArgumentException("Protected method: " + sig1);
            throw e;
        }
    }

會(huì)用f1(之前invokeSuper用的f2),f1的代碼參照上面列舉出來(lái)的對(duì)應(yīng)邏輯

//getIndex中say方法前面返回0作為index

public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }

也就是說(shuō)serviceImpl.say()通過(guò)invoke(非invokeSuper)最終還是回到了serviceImpl.say(),變成了遞歸導(dǎo)致堆棧溢出求泰。

5.methodProxy和fastClass結(jié)合使用
methodProxy用于生成方法代理的關(guān)系綁定(classA.methodA被classB.methodB代理)
fastClass用于完成方法代理的快速調(diào)用央渣,通過(guò)簽名拿到標(biāo)識(shí)index,避免重復(fù)反射

吐槽

1.methodProxy負(fù)責(zé)了fastClass的生成渴频,但是methodProxy多次調(diào)用生成fastClass芽丹,還要讓fastClass最終只有一份class文件
也就是調(diào)用代理方法時(shí),再創(chuàng)建fastEnhance類(lèi)卜朗,再轉(zhuǎn)發(fā)過(guò)去
這種lazy的思想感覺(jué)超出了自己的職責(zé)

2.c1,c2,f1,f2的關(guān)系有點(diǎn)繞

3.文檔少

4.反編譯用http://www.javadecompilers.com/
方式選擇CFR (very good and well-supported decompiler for Java 8)
不要選Jadx, fast and with Android support
否則代碼會(huì)讓人誤解拔第,比如

//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()函數(shù)在不同反編譯方式下

//CFR (very good and well-supported decompiler for Java 8)方式

    final void CGLIB$say$0() {
        super.say();//即CglibLearn.serviceImpl.say()方法
    }

//Jadx, fast and with Android support
    final void CGLIB$say$0() {
        say();//super沒(méi)了!!!讓我以為invokeSuper函數(shù)最終也會(huì)產(chǎn)生遞歸!!!
    }

refer

http://www.cnblogs.com/cruze/p/3865180.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咕村,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蚊俺,更是在濱河造成了極大的恐慌懈涛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泳猬,死亡現(xiàn)場(chǎng)離奇詭異批钠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)得封,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)埋心,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人忙上,你說(shuō)我怎么就攤上這事拷呆。” “怎么了疫粥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵茬斧,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我手形,道長(zhǎng)啥供,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任库糠,我火速辦了婚禮,結(jié)果婚禮上涮毫,老公的妹妹穿的比我還像新娘瞬欧。我一直安慰自己,他們只是感情好罢防,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布艘虎。 她就那樣靜靜地躺著,像睡著了一般咒吐。 火紅的嫁衣襯著肌膚如雪野建。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,184評(píng)論 1 308
  • 那天恬叹,我揣著相機(jī)與錄音候生,去河邊找鬼。 笑死绽昼,一個(gè)胖子當(dāng)著我的面吹牛唯鸭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硅确,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼目溉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼明肮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缭付,我...
    開(kāi)封第一講書(shū)人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柿估,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后陷猫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體官份,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年烙丛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舅巷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡河咽,死狀恐怖钠右,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忘蟹,我是刑警寧澤飒房,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站媚值,受9級(jí)特大地震影響狠毯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜褥芒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一嚼松、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锰扶,春花似錦献酗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至京闰,卻和暖如春颜及,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹂楣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工俏站, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捐迫。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓乾翔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子反浓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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