徹底理解動(dòng)態(tài)代理

本文篇幅比較長(zhǎng)吠各,在確定您是否需要仔細(xì)閱讀本文前搂捧,可以先思考一下下面幾個(gè)問(wèn)題:

  1. 動(dòng)態(tài)代理是什么纯出?
  2. 如何實(shí)現(xiàn)動(dòng)態(tài)代理?
  3. 所有類(lèi)都能實(shí)現(xiàn)動(dòng)態(tài)代理嗎对人?
  4. 非目標(biāo)方法是否會(huì)被代理?
  5. 為什么 JDK 實(shí)現(xiàn)動(dòng)態(tài)代理必須要求被代理類(lèi)實(shí)現(xiàn)接口拂共?
  6. 為什么 CGLib 實(shí)現(xiàn)動(dòng)態(tài)代理要求被代理類(lèi)為非 final類(lèi)牺弄?

為了理解動(dòng)態(tài)代理,我們需要先了解代理模式是怎么回事宜狐。

代理模式

代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象势告,并由代理對(duì)象控制對(duì)原對(duì)象的引用「Ш悖可以將代理模式理解為生活中常見(jiàn)的中介咱台,UML 圖如下。

proxy.png
public interface Subject {
    public void doOperation();
}
public class RealSubject implements Subject {
    @Override
    public void doOperation() {
        System.out.println("RealSubject doOperation...");
    }
}
public class Proxy implements Subject {
    private Subject subject;
    public Proxy(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void doOperation() {
        System.out.println("Proxy before RealSubject doOperation...");
        subject.doOperation();
        System.out.println("Proxy after RealSubject doOperation...");
    }
}
public class Client {
    public static void main(String[] args) {
        Subject subject = new Proxy(new RealSubject());
        subject.doOperation();
    }
}

上述代碼輸出如下:

Proxy before RealSubject doOperation...
RealSubject doOperation...
Proxy after RealSubject doOperation...

通過(guò)代理模式俭驮,我們可以做到在不修改目標(biāo)對(duì)象的前提下,對(duì)目標(biāo)對(duì)象進(jìn)行功能擴(kuò)展回溺。但是上述靜態(tài)代理的不足之處在于需要事先寫(xiě)好相應(yīng)的代理類(lèi),而且在接口發(fā)生變化時(shí)需要對(duì)被代理類(lèi)及代理類(lèi)進(jìn)行修改混萝,對(duì)此 Java 引入了動(dòng)態(tài)代理的概念遗遵。

動(dòng)態(tài)代理

與靜態(tài)代理需要事先構(gòu)建不同,動(dòng)態(tài)代理是動(dòng)態(tài)地在內(nèi)存中生成的逸嘀。一般而言车要,動(dòng)態(tài)代理可以由 JDK 動(dòng)態(tài)代理及CGLib 動(dòng)態(tài)代理實(shí)現(xiàn)。

JDK動(dòng)態(tài)代理

使用JDK動(dòng)態(tài)代理只需要3步即可完成:

  1. 創(chuàng)建被代理的對(duì)象 RealSubject
  2. 創(chuàng)建被代理對(duì)象的處理對(duì)象崭倘,持有目標(biāo)(被代理)對(duì)象 JDKInvocationHandler
  3. 使用Proxy的靜態(tài)方法 newProxyInstance 創(chuàng)建代理對(duì)象
public class JDKInvocationHandler implements java.lang.reflect.InvocationHandler {
    private Subject subject;
    public JDKInvocationHandler(Subject subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDKProxy before RealSubject doOperation...");
        Object ret = method.invoke(subject, args);
        System.out.println("JDKProxy after RealSubject doOperation...");
        return ret;
    }
}
public class Client {
    public static void main(String[] args) {
        //創(chuàng)建被代理的對(duì)象 realSubject
        RealSubject realSubject = new RealSubject();
        //創(chuàng)建被代理對(duì)象的處理對(duì)象
        InvocationHandler handler = new JDKInvocationHandler(realSubject);
        //創(chuàng)建代理對(duì)象
        Subject proxy = (Subject) Proxy.newProxyInstance(
            RealSubject.class.getClassLoader(),
            RealSubject.class.getInterfaces(),
            handler);
        //執(zhí)行相應(yīng)的方法
        proxy.doOperation();
    }
}

上述代碼輸出如下:

JDKProxy before RealSubject doOperation...
RealSubject doOperation...
JDKProxy after RealSubject doOperation...

其中翼岁,InvocationHandler 是 Java 自帶的接口,其定義如下:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Proxy 靜態(tài)方法的定義如下:

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

其中司光,

  1. loader 為類(lèi)加載器登澜,出于安全性,要求 loader 對(duì) interfaces 可見(jiàn)飘庄,通常使用被代理類(lèi)的ClassLoader。
  2. interfaces 為被代理對(duì)象需要實(shí)現(xiàn)的所有接口购撼。
  3. h為方法調(diào)用的實(shí)際處理者跪削,通過(guò) InvocationHandler 對(duì)被代理類(lèi)進(jìn)行拓展。

(ps:類(lèi)加載器后面會(huì)有專(zhuān)門(mén)介紹迂求。)

但是碾盐,等一下,為什么需要這三個(gè)參數(shù)呢揩局?是不是任意一個(gè) Java 類(lèi)都可以動(dòng)態(tài)代理呢毫玖?我們不妨深入看看 Proxy類(lèi)到底做了什么?Proxy.newProxyInstance 方法的主要內(nèi)容(刪除了安全檢測(cè)等內(nèi)容)如下:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
                   InvocationHandler h) throws IllegalArgumentException{
    final Class<?>[] intfs = interfaces.clone();
    //根據(jù)ClassLoader及接口獲取指定的代理類(lèi)的Class信息
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        //在Proxy中 constructorParams被硬編碼為{InvocationHandler.class};
        //獲取代理類(lèi)的參數(shù)為 InvocationHandler 的構(gòu)造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        //生成代理類(lèi)
        return cons.newInstance(new Object[]{h});
    } catch (XXException e) {
        throw new XXException(e.toString(), e);
    }
}
//從proxyClassCache中獲取代理類(lèi)的Class信息,如果沒(méi)有則根據(jù)classLoader付枫、ingerfaces生成加載并緩存
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
        return proxyClassCache.get(loader, interfaces);
}

動(dòng)態(tài)編譯生成代理類(lèi)的代碼如下:

//通過(guò)反射動(dòng)態(tài)生成烹玉、編譯代理類(lèi),得到代理類(lèi)的字節(jié)碼數(shù)據(jù)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                                    proxyName,interfaces, accessFlags);
try {
    //動(dòng)態(tài)加載代理類(lèi)
    return defineClass0(loader,proxyName,proxyClassFile,0,proxyClassFile.length);
} catch (ClassFormatError e) {
    throw new IllegalArgumentException(e.toString());
}

至此阐滩,我們知道了 Proxy 的運(yùn)行邏輯二打,為了進(jìn)一步了解動(dòng)態(tài)生成的代理類(lèi)的內(nèi)容,我們不妨輸出并使用反編譯工具查看動(dòng)態(tài)生成的代理類(lèi)的信息:

通過(guò)在程序運(yùn)行時(shí)設(shè)置:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

即可將生成的代理類(lèi)保存在項(xiàng)目根目錄下掂榔,路徑為:com/sun/proxy/$ProxyNum.class

使用Java Decompiler工具查看继效,結(jié)果如下:

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    @Override
    public final boolean equals(Object paramObject) {
        try {
            return ((Boolean) this.h.invoke(this, m1, 
                            new Object[] { paramObject })).booleanValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    @Override
    public final String toString() {
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    @Override
    public final void doOperation() {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    @Override
    public final int hashCode() {
        try {
            return ((Integer) this.h.invoke(this, m0, null)).intValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.iqts.proxy.Subject").getMethod("doOperation", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

到這里可以大概推測(cè)出,JDK 動(dòng)態(tài)代理是通過(guò)繼承 Proxy 類(lèi)装获,實(shí)現(xiàn)被代理類(lèi)的所有接口生成動(dòng)態(tài)代理類(lèi)瑞信。這也解釋了采用 JDK 動(dòng)態(tài)代理時(shí)為什么只能使用接口引用指向代理,而不能使用被代理的具體類(lèi)引用指向代理穴豫。

Subject proxy = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
                RealSubject.class.getInterfaces(), handler);//ok

//java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to RealSubject
RealSubject proxy = (RealSubject) Proxy.newProxyInstance(
                RealSubject.class.getClassLoader(),
                RealSubject.class.getInterfaces(), handler);

此外凡简,除了實(shí)現(xiàn)了被代理所有接口中的方法外,JDK 動(dòng)態(tài)代理還重寫(xiě)了 Object 類(lèi)中的 hashCode绩郎、equals潘鲫、toString 三個(gè)方法。

為了更好地看出類(lèi)之間的依賴(lài)關(guān)系肋杖,上述代碼可以簡(jiǎn)化如下:

public interface Subject {
    public void doOperation();
}
public class RealSubject implements Subject {
    @Override
    public void doOperation() {
        System.out.println("RealSubject doOperation...");
    }
}
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {
        this.h = h;
    }
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h){
        Class<?> cl = getProxyClass0(loader, intfs);
        final Constructor<?> cons = cl.getConstructor(InvocationHandler.class);
        return cons.newInstance(new Object[]{h});
    }
}
public final class $Proxy0 extends Proxy implements Subject {
    private static Method m3;
    public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }
    @Override
    public final void doOperation() {
        try {
            this.h.invoke(this, m3, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
}

到這里我們就不難理解溉仑,為什么 JDK 實(shí)現(xiàn)動(dòng)態(tài)代理必須要求被代理類(lèi)實(shí)現(xiàn)接口,這是由于動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類(lèi)需要繼承 Proxy 類(lèi)状植,而 Java 中只能單繼承的限制使得被代理類(lèi)必須實(shí)現(xiàn)接口才能實(shí)現(xiàn)動(dòng)態(tài)代理浊竟。

JDK 能夠很好地實(shí)現(xiàn)動(dòng)態(tài)代理,但是如果被代理的類(lèi)沒(méi)有實(shí)現(xiàn)接口就無(wú)法實(shí)現(xiàn)動(dòng)態(tài)代理津畸,這時(shí)候我們就需要使用第三方工具來(lái)幫忙了振定。

CGLib

CGLib 是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展 Java 類(lèi)及實(shí)現(xiàn)Java接口肉拓、提供方法的攔截后频,因此被眾多 AOP 框架使用。CGLib 包的底層是通過(guò)使用字節(jié)碼處理框架 ASM 來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類(lèi)暖途。

使用 CGLib 實(shí)現(xiàn)動(dòng)態(tài)代理也很簡(jiǎn)單卑惜,首先

  1. 創(chuàng)建Enhancer對(duì)象

  2. 設(shè)置被代理類(lèi)

  3. 回調(diào)對(duì)象(回調(diào)類(lèi)實(shí)現(xiàn) MethodInterceptor或InvocationHandler接口)

  4. 創(chuàng)建并設(shè)置回調(diào)對(duì)象

  5. 創(chuàng)建代理對(duì)象

public class CGLib {
    public static void main(String[] args) {
        // 創(chuàng)建Enhancer對(duì)象
        Enhancer enhancer = new Enhancer();
        // 設(shè)置被代理類(lèi)
        enhancer.setSuperclass(ConcreteSubject.class);
        
        // 創(chuàng)建回調(diào)對(duì)象
        //實(shí)現(xiàn) MethodInterceptor 接口
        Callback callback = new CGLibMethodInterceptor();
        //實(shí)現(xiàn) InvocationHandler 接口
        Callback callback = new CGLibInvocationHandler(new ConcreteSubject());
        
        // 設(shè)置回調(diào)對(duì)象
        enhancer.setCallback(callback);
        // 創(chuàng)建代理對(duì)象
        ConcreteSubject subject = (ConcreteSubject) enhancer.create();
        subject.doOperation();
    }
}
//回調(diào)對(duì)象 實(shí)現(xiàn) MethodInterceptor
public class CGLibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) 
        throws Throwable {
        Object ret = null;
        System.out.println("CGLib before ConcreteSubject doOperation...");
        ret = proxy.invokeSuper(obj, args);
        System.out.println("CGLib after ConcreteSubject doOperation...");
        return ret;
    }
}
//回調(diào)對(duì)象 實(shí)現(xiàn) InvocationHandler
public class CGLibInvocationHandler implements InvocationHandler {
    
        private Object realSubject;

        public CGLibInvocationHandler(Object realSubject) {
            super();
            this.realSubject = realSubject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
            System.out.println("CGLib before ConcreteSubject doOperation...");
            Object ret = method.invoke(realSubject, args);
            System.out.println("CGLib after ConcreteSubject doOperation...");
            return ret;
        }
    }
//被代理類(lèi)
public class ConcreteSubject {
    public void doOperation() {
        System.out.println("ConcreteSubject doOperation...");
    }
}

輸出如下:

CGLib before ConcreteSubject doOperation...
ConcreteSubject doOperation...
CGLib after ConcreteSubject doOperation...

為了進(jìn)一步理解 CGLib 動(dòng)態(tài)代理的生成機(jī)制,我們不妨將生成的動(dòng)態(tài)代理類(lèi)保存到文件中驻售,可以通過(guò)設(shè)置:

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://CGLib//proxy//");

來(lái)導(dǎo)出動(dòng)態(tài)代理類(lèi)露久,使用Java Decompiler工具查看,結(jié)果如下:

public class ConcreteSubject$$EnhancerByCGLib$$7e8b8caf
    extends ConcreteSubject implements Factory {
    
    private MethodInterceptor CGLib$CALLBACK_0;

    final void CGLib$doOperation$0() {
        super.doOperation();
    }
    
    @Override
    public final void doOperation(){
        MethodInterceptor tmp4_1 = this.CGLib$CALLBACK_0;
        if (tmp4_1 == null){
          tmp4_1;
          CGLib$BIND_CALLBACKS(this);
        }
        if (this.CGLib$CALLBACK_0 != null) {
          return;
        }
        super.doOperation();
    }
    @Override
    public final boolean equals(Object paramObject)...
    @Override
    public final String toString()...
    @Override
    public final int hashCode()...
    @Override
    public final Object clone()...
}

通過(guò)反編譯得到代碼可以看出欺栗,CGLib 是通過(guò)繼承被代理類(lèi) ConcreteSubject 實(shí)現(xiàn)動(dòng)態(tài)代理的毫痕,這也就要求被代理的類(lèi)不能是 final 類(lèi)征峦。此外,與 JDK 動(dòng)態(tài)代理相比消请,CGLib 不僅重寫(xiě)了 Object 類(lèi)的 hashCode栏笆、equals、toString方法梯啤,還重寫(xiě)了 clone 方法竖伯。

此外,值得注意的是創(chuàng)建回調(diào)對(duì)象時(shí)因宇,采用實(shí)現(xiàn) MethodInterceptor 接口與 InvocationHandler 接口這兩種方式除了是否需要額外創(chuàng)建被代理對(duì)象以及方法調(diào)用的差異外七婴,還有一個(gè)小細(xì)節(jié):采用實(shí)現(xiàn) InvocationHandler 接口的方式生成的代理類(lèi)在調(diào)用的方法內(nèi)部如果還調(diào)用該代理類(lèi)的其他成員方法時(shí),會(huì)對(duì)被調(diào)用的其他方法進(jìn)行代理察滑,而采用 MethodInterceptor 接口方式不會(huì)打厘。(ps: JDK 動(dòng)態(tài)代理也不會(huì)對(duì)非目標(biāo)方法進(jìn)行代理。)

為被代理類(lèi)添加兩個(gè)方法:

public class ConcreteSubject {

    public static void fn() {
        System.out.println("fn");
    }

    public void doOperation() {
        System.out.println("ConcreteSubject doOperation...");
        other();// InvocationHandler 同時(shí)會(huì)代理非目標(biāo)方法
        fn();//靜態(tài)方法不代理
    }

    public void other() {
        System.out.println("ConcreteSubject other...");
    }
}

相應(yīng)的輸出如下:

invocationHandler
CGLib before RealSubject doOperation...
ConcreteSubject doOperation...
ConcreteSubject other...
fn
CGLib after RealSubject doOperation...
==================
methodInterceptor
Cglib before ConcreteSubject doOperation...
ConcreteSubject doOperation...
Cglib before ConcreteSubject doOperation...
ConcreteSubject other...
Cglib after ConcreteSubject doOperation...
fn
Cglib after ConcreteSubject doOperation...

CGLib 生成動(dòng)態(tài)代理的兩種方式的區(qū)別總結(jié)如下:

項(xiàng)目 MethodInterceptor InvocationHandler
是否依賴(lài)被代理對(duì)象實(shí)例 不依賴(lài) 依賴(lài)
目標(biāo)方法執(zhí)行方式 method.invokeSuper(proxy, args) method.invoke(realSubject, args);
非目標(biāo)方法是否進(jìn)行代理 不代理 代理

JDK 與 CGLib 動(dòng)態(tài)代理的區(qū)別

結(jié)合 JDK 動(dòng)態(tài)代理的實(shí)現(xiàn)贺辰,可以得出下列區(qū)別:

項(xiàng)目 JDK CGLib
被代理對(duì)象的要求 必須實(shí)現(xiàn)接口(可為 final 類(lèi)) 非final類(lèi)
代理類(lèi)生成方式 繼承 Proxy户盯,實(shí)現(xiàn)被代理類(lèi)的所有接口 繼承被代理類(lèi),實(shí)現(xiàn) Factory 接口
非目標(biāo)方法是否進(jìn)行代理 不進(jìn)行代理 可通過(guò) InvocationHandler 進(jìn)行代理

至此饲化,動(dòng)態(tài)代理兩種實(shí)現(xiàn)方式及其原理已經(jīng)介紹完畢莽鸭,在理解了相關(guān)原理后,我們完全可以通過(guò)反射及Java動(dòng)態(tài)編譯技術(shù)實(shí)現(xiàn)動(dòng)態(tài)代理吃靠。

既然JDK動(dòng)態(tài)代理要求被代理類(lèi)必須實(shí)現(xiàn)接口硫眨,而CGLib要求被代理類(lèi)不能是final類(lèi),那么能不能為沒(méi)有實(shí)現(xiàn)接口的final類(lèi)進(jìn)行動(dòng)態(tài)代理呢巢块?

答案是不能礁阁,但是可以通過(guò)反射來(lái)實(shí)現(xiàn)類(lèi)似動(dòng)態(tài)代理的功能,只需要將Proxy進(jìn)行改造即可族奢,如:

public class MyProxy {
    protected InvocationHandler invocationHandler;
    public MyProxy(InvocationHandler invocationHandler) {
        super();
        this.invocationHandler = invocationHandler;
    }
    public static Object newProxyInstance(ClassLoader loader, 
                                          Class<?> clz, InvocationHandler h){
        ...
    }
    public Object invoke(String methodName, Object... args){
        Class<?>[] parameterTypes = getParameterTypes(args);
        Method method = this.getClass().getDeclaredMethod(methodName,parameterTypes);
        return method.invoke(this, args);
    }
}

使用時(shí)通過(guò) MyProxy 的 invoke 方法實(shí)現(xiàn)被代理對(duì)象方法的調(diào)用:

MyProxy proxy = (MyProxy) MyProxy.newProxyInstance(
                                        FinalSubject.class.getClassLoader(),
                                        FinalSubject.class,
                                        new FinalInvocationHandler(new FinalSubject()));
proxy.invoke("doOperation");
proxy.invoke("other",args);

完整項(xiàng)目可以參考本人的 github 小項(xiàng)目 DynamicProxy

update: 2019-03-10 11:04:25

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末姥闭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子越走,更是在濱河造成了極大的恐慌棚品,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廊敌,死亡現(xiàn)場(chǎng)離奇詭異南片,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)庭敦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)薪缆,“玉大人秧廉,你說(shuō)我怎么就攤上這事伞广。” “怎么了疼电?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嚼锄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蔽豺,道長(zhǎng)区丑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任修陡,我火速辦了婚禮沧侥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘魄鸦。我一直安慰自己宴杀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拾因。 她就那樣靜靜地躺著旺罢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绢记。 梳的紋絲不亂的頭發(fā)上扁达,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蠢熄,去河邊找鬼跪解。 笑死,一個(gè)胖子當(dāng)著我的面吹牛护赊,可吹牛的內(nèi)容都是我干的惠遏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骏啰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼节吮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起判耕,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤透绩,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后壁熄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體帚豪,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年草丧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸臣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昌执,死狀恐怖烛亦,靈堂內(nèi)的尸體忽然破棺而出诈泼,到底是詐尸還是另有隱情,我是刑警寧澤煤禽,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布铐达,位于F島的核電站,受9級(jí)特大地震影響檬果,放射性物質(zhì)發(fā)生泄漏瓮孙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一选脊、第九天 我趴在偏房一處隱蔽的房頂上張望杭抠。 院中可真熱鬧,春花似錦知牌、人聲如沸祈争。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)菩混。三九已至,卻和暖如春扁藕,著一層夾襖步出監(jiān)牢的瞬間沮峡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工亿柑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邢疙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓望薄,卻偏偏與公主長(zhǎng)得像疟游,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痕支,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355