JDK動(dòng)態(tài)代理為什么不能代理類--詳解動(dòng)態(tài)代理

java進(jìn)階系列-CLassLoader詳解
java進(jìn)階系列-反射詳解
java進(jìn)階系列-動(dòng)態(tài)代理

動(dòng)態(tài)代理的應(yīng)用十分廣泛男摧,很多有名的框架都用到了動(dòng)態(tài)代理刑棵,比如spring aop漆际,mybatis柠新,Hibernate,rpc等等,甚至我們?nèi)粘i_發(fā)中一些非功能性需求--監(jiān)控殊者、 統(tǒng)計(jì)、鑒權(quán)验夯、限流猖吴、事務(wù)、冪等挥转、日志--也是基于動(dòng)態(tài)代理實(shí)現(xiàn)的海蔽。由此可見,掌握動(dòng)態(tài)代理對(duì)我們的開發(fā)工作或閱讀框架源碼是非常有幫助的绑谣。

本文主要介紹Java中兩種常見的動(dòng)態(tài)代理方式:JDK動(dòng)態(tài)代理CGLIB動(dòng)態(tài)代理党窜。

Java動(dòng)態(tài)代理與java反射關(guān)系緊密,若讀者對(duì)Java反射機(jī)制有些疑問借宵,可參考上一篇文章《java進(jìn)階系列-反射詳解》幌衣。

本文概要:

  1. 簡(jiǎn)單介紹代理模式
  2. 比較靜態(tài)代理與動(dòng)態(tài)代理
  3. JDK動(dòng)態(tài)代理
  4. CGLIB動(dòng)態(tài)代理

引入需求

UserServiceImpl代碼如下:

public class UserServiceImpl implements UserService {

    @Override
    public void login(String username, String password) {
        System.out.println("歡迎" + username + "登錄!");
    }

}

可以看到UserServiceImpl實(shí)現(xiàn)了UserService壤玫,有一個(gè)login方法豁护。

請(qǐng)你設(shè)想一下,現(xiàn)在我們面臨這樣一個(gè)新需求:收集接口請(qǐng)求的原始數(shù)據(jù)欲间,比如記錄方法的訪問時(shí)間楚里,及處理時(shí)長(zhǎng)。

首先分析一下需求猎贴,很明顯這樣的需求與業(yè)務(wù)代碼本身并沒有什么關(guān)系班缎,任何業(yè)務(wù)代碼都可能面臨這種需求,所以直接在login方法中添加代碼的方法她渴,破壞了業(yè)務(wù)類的單一原則达址,也增加了代碼的冗余程度。

為了將框架代碼和業(yè)務(wù)代碼解耦惹骂,代理模式就派上用場(chǎng)了苏携。

代理模式

代理模式(Proxy Design Pattern):在不改變?cè)碱?(或叫被代理類)代碼的情況下,通過引入代理類來給原始類附加功能对粪。
如果根據(jù)代理類字節(jié)碼的創(chuàng)建時(shí)機(jī)來分類右冻,可以分為靜態(tài)代理動(dòng)態(tài)代理

  • 靜態(tài):在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和原始類的關(guān)系在運(yùn)行前就確定了著拭。
  • 動(dòng)態(tài):代理類源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成纱扭,所以在運(yùn)行前并不存在代理類的字節(jié)碼文件

靜態(tài)代理

我們首先實(shí)現(xiàn)靜態(tài)代理,添加代理類UserServiceProxy儡遮,同樣實(shí)現(xiàn)UserService接口乳蛾,代理類 UserServiceProxy 負(fù)責(zé)在業(yè)務(wù)代碼執(zhí)行前后附加其他邏輯代碼,并通過委托的方式調(diào)用原始類來執(zhí)行業(yè)務(wù)代碼。具體的代碼實(shí)現(xiàn)如下所示:

public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void login(String username, String password) {
        long startTimestamp = System.currentTimeMillis();

        userService.login(username, password);

        long endTimeStamp = System.currentTimeMillis();

        long responseTime = endTimeStamp - startTimestamp;

        System.out.printf("method:%s, startTime:%s, responseTime:%s", "login", startTimestamp, responseTime);
    }

}

下面調(diào)用一下試試:

public class DynamicProxyDemo {
    public static void main(String[] args) {
        staticProxy();
    }

    private static void staticProxy() {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
        userServiceProxy.login("rex", "123");
    }
}

執(zhí)行結(jié)果如下:

歡迎rex登錄肃叶!
method:login, startTime:1605756985465, responseTime:1

雖然靜態(tài)代理實(shí)現(xiàn)簡(jiǎn)單蹂随,且不侵入原代碼,但缺點(diǎn)也是很明顯的因惭,試想一下岳锁,隨著我們業(yè)務(wù)系統(tǒng)的逐漸發(fā)展,代碼越來越多蹦魔,難道我們要為每個(gè)原始類都添加一個(gè)代理類嗎激率?

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):代碼結(jié)構(gòu)簡(jiǎn)單,較容易實(shí)現(xiàn)
  • 缺點(diǎn):無法適配所有代理場(chǎng)景勿决,如果有新的需求乒躺,需要修改代理類,「不符合軟件工程的開閉原則」

我們可以使用動(dòng)態(tài)代理(Dynamic Proxy)來解決這個(gè)問題低缩。

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

JDK動(dòng)態(tài)代理嘉冒,有兩個(gè)關(guān)鍵類:java.lang.reflect.InvocationHandlerjava.lang.reflect.Proxy
具體的代碼如下所示表制。

package dynamicproxy;

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

public class DynamicProxyHandler implements InvocationHandler {
    // 原始類
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTimestamp = System.currentTimeMillis();

        Object obj = method.invoke(proxied, args);

        long endTimeStamp = System.currentTimeMillis();

        long responseTime = endTimeStamp - startTimestamp;

        System.out.printf("method:%s, startTime:%s, responseTime:%s", method.getName(), startTimestamp, responseTime);

        return obj;
    }
}

invoke() 方法有3個(gè)參數(shù):

  • Object proxy:代理對(duì)象
  • Method method:真正執(zhí)行的方法
  • Object[] agrs:調(diào)用第二個(gè)參數(shù) method 時(shí)傳入的參數(shù)列表值

生成代理對(duì)象需要用到Proxy類健爬,它可以幫助我們生成任意一個(gè)代理對(duì)象,里面提供一個(gè)靜態(tài)方法newProxyInstance么介。

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

實(shí)例化代理對(duì)象時(shí)娜遵,需要傳入3個(gè)參數(shù):

  • ClassLoader loader:加載動(dòng)態(tài)代理類的類加載器
  • Class<?>[] interfaces:代理類實(shí)現(xiàn)的接口,可以傳入多個(gè)接口
  • InvocationHandler h:指定代理類的「調(diào)用處理程序」壤短,即調(diào)用接口中的方法時(shí)设拟,會(huì)找到該代理工廠h,執(zhí)行invoke()方法
public class DynamicProxyDemo {
    public static void main(String[] args) {
        dynamicProxy();
    }
    
    private static void dynamicProxy() {
        // 設(shè)置變量可以保存動(dòng)態(tài)代理類久脯,默認(rèn)名稱以 $Proxy0 格式命名
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        UserServiceImpl userServiceImpl = new UserServiceImpl();

        UserService proxyInstance = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),
                userServiceImpl.getClass().getInterfaces(), new DynamicProxyHandler(userServiceImpl));

        proxyInstance.login("jack", "456");
    }
}

執(zhí)行結(jié)果如下:

歡迎jack登錄纳胧!
method:login, startTime:1605759941043, responseTime:0

以上就是我們實(shí)現(xiàn)的動(dòng)態(tài)代理了,相比靜態(tài)代理帘撰,我們不需要重復(fù)實(shí)現(xiàn)代理類跑慕,jdk會(huì)幫助我們動(dòng)態(tài)的實(shí)現(xiàn)代理類。
看下上述代碼的調(diào)用棧信息:

在這里插入圖片描述

其中摧找,Proxy0 就是自動(dòng)生成的代理類核行,如果我們想看下自動(dòng)生成proxy class到底長(zhǎng)什么樣該如何辦呢?可以通過參數(shù)-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true(或者執(zhí)行System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");)來保存成對(duì)應(yīng)的class文件蹬耘。Proxy0 代碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import dynamicproxy.UserService;
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 UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void login(String var1, String var2) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("dynamicproxy.UserService").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

從 $Proxy0 的代碼中我們可以發(fā)現(xiàn):

  • $Proxy0 繼承了 Proxy 類(這也是JDK動(dòng)態(tài)代理為什么不能代理類的直接原因)芝雪,并且實(shí)現(xiàn)了被代理的所有接口,以及equals综苔、hashCode惩系、toString等方法
  • $Proxy0 繼承了 Proxy 類位岔, 每個(gè)代理類都有一個(gè)參數(shù)為 InvocationHandler的構(gòu)造方法
  • 類和所有方法都被 public final 修飾,所以代理類只可被使用堡牡,不可以再被繼承
  • 每個(gè)方法都有一個(gè) Method 對(duì)象來描述抒抬,Method 對(duì)象在static靜態(tài)代碼塊中創(chuàng)建,以 m + 數(shù)字 的格式命名
  • 調(diào)用方法的時(shí)候通過 super.h.invoke(this, m1, (Object[])null); 調(diào)用晤柄,其中的 super.h.invoke 實(shí)際上是在創(chuàng)建代理的時(shí)候傳遞給 Proxy.newProxyInstance 的 DynamicProxyHandler 對(duì)象瞧剖,它繼承 InvocationHandler 類,負(fù)責(zé)實(shí)際的調(diào)用處理邏輯

而 DynamicProxyHandler 的 invoke 方法接收到 method可免、args 等參數(shù)后,進(jìn)行一些處理做粤,然后通過反射讓被代理的對(duì)象 proxied 執(zhí)行方法浇借。

Proxy源碼

下面開始分析JDK動(dòng)態(tài)代理生成proxy class流程源碼,Proxy.newProxyInstance() 是生成動(dòng)態(tài)代理對(duì)象的關(guān)鍵怕品,我們可來看看它里面到底干了些什么妇垢,以下代碼分析只貼出主要流程代碼。

 /** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
        { InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        // 省略檢驗(yàn)部分代碼

        /*
         * 這里是為了得到動(dòng)態(tài)生成的代理類肉康,具體過程稍后分析
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            /*
             * 因?yàn)閯?dòng)態(tài)生成的代理類都繼承了Proxy闯估,所以都有一個(gè)參數(shù)為InvocationHandler的構(gòu)造器
             * 通過這個(gè)構(gòu)造器生成代理類的實(shí)例
             */
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            return cons.newInstance(new Object[]{h});
        } 
        // 省略catch部分代碼
    }

可以看到代理類是getProxyClass0方法獲取的:

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

發(fā)現(xiàn)里面用到一個(gè)緩存 「proxyClassCache」,從結(jié)構(gòu)來看類似于是一個(gè) map 結(jié)構(gòu)吼和,根據(jù)類加載器loader和真實(shí)對(duì)象實(shí)現(xiàn)的接口interfaces查找是否有對(duì)應(yīng)的 Class 對(duì)象涨薪,我們接著往下看 get() 方法。

public V get(K key, P parameter) {
    ...
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    ...
}

在 get() 方法中炫乓,如果沒有從緩存中獲取到 Class 對(duì)象刚夺,則需要利用 「subKeyFactory」 去實(shí)例化一個(gè)動(dòng)態(tài)代理對(duì)象,subKeyFactory 在這里是「Proxy」 類中「ProxyClassFactory」 內(nèi)部類末捣,由它來創(chuàng)建一個(gè)動(dòng)態(tài)代理類侠姑,所以我們接著去看 ProxyClassFactory 中的 apply() 方法。

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
     //自動(dòng)生成一個(gè)proxy class箩做,序號(hào)+1
    long num = nextUniqueNumber.getAndIncrement();
    // 動(dòng)態(tài)代理對(duì)象名拼接莽红!包名 + "$Proxy" + 數(shù)字
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    // 生成字節(jié)碼文件,返回一個(gè)字節(jié)數(shù)組
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
    // 利用字節(jié)碼文件創(chuàng)建該字節(jié)碼的 Class 類對(duì)象
        return defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
    }
}

apply() 方法中注意有兩個(gè)非常重要的方法:

  • ProxyGenerator.generateProxyClass():它是生成字節(jié)碼文件的方法邦邦,它返回了一個(gè)字節(jié)數(shù)組安吁,字節(jié)碼文件本質(zhì)上就是一個(gè)字節(jié)數(shù)組,所以 proxyClassFile數(shù)組就是一個(gè)字節(jié)碼文件
  • defineClass0():生成字節(jié)碼文件的 Class 對(duì)象圃酵,它是一個(gè) native 本地方法柳畔,調(diào)用操作系統(tǒng)底層的方法創(chuàng)建類對(duì)象

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

CGLIB(Code generation Library) 不是 JDK 自帶的動(dòng)態(tài)代理,它需要導(dǎo)入第三方依賴郭赐,它是一個(gè)字節(jié)碼生成類庫(kù)薪韩,能夠在運(yùn)行時(shí)動(dòng)態(tài)生成代理類确沸。

與 JDK 動(dòng)態(tài)代理不同的是,CGLIB不僅能夠?yàn)?Java接口 做代理俘陷,而且能夠?yàn)?strong>普通的 Java類 做代理罗捎。

添加maven依賴:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib-nodep</artifactId>
  <version>3.3.0</version>
</dependency>

CGLIB 代理,有兩個(gè)核心的類:MethodInterceptor接口和Enhancer類拉盾,MethodInterceptor類似于上述的InvocationHandler桨菜,Enhancer類似于Proxy。
下面是具體實(shí)現(xiàn):

package dynamicproxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DynamicInterceptor implements MethodInterceptor {

    /**
     * @param o 表示原始類
     * @param method 表示被攔截的方法
     * @param objects 數(shù)組表示參數(shù)列表捉偏,基本數(shù)據(jù)類型需要傳入其包裝類型倒得,如int-->Integer、long-Long夭禽、double-->Double
     * @param methodProxy 表示對(duì)方法的代理霞掺,invokeSuper方法表示對(duì)原始對(duì)象方法的調(diào)用
     * @return 執(zhí)行結(jié)果
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long startTimestamp = System.currentTimeMillis();

        // 注意這里是調(diào)用 invokeSuper 而不是 invoke,否則死循環(huán)讹躯,
        // methodProxy.invokeSuper執(zhí)行的是原始類的方法菩彬,
        // method.invoke執(zhí)行的是子類的方法
        Object result = methodProxy.invokeSuper(o, objects);

        long endTimeStamp = System.currentTimeMillis();

        long responseTime = endTimeStamp - startTimestamp;

        System.out.printf("method:%s, startTime:%s, responseTime:%s", method.getName(), startTimestamp, responseTime);

        return result;
    }

}

在演示Enhancer前,我們先添加UserServiceWithoutImpl(不再實(shí)現(xiàn)UserService接口):

package dynamicproxy;

public class UserServiceWithoutImpl {

    public void login(String username, String password) {
        System.out.println("歡迎" + username + "登錄潮梯!");
    }

}

測(cè)試

public class DynamicProxyDemo {
    public static void main(String[] args) {
        cglibProxy();
    }
    private static void cglibProxy() {
        DynamicInterceptor interceptor = new DynamicInterceptor();

        Enhancer enhancer = new Enhancer();
        // 設(shè)置超類骗灶,cglib是通過繼承來實(shí)現(xiàn)的
        enhancer.setSuperclass(UserServiceWithoutImpl.class);  
        enhancer.setCallback(interceptor);

        UserServiceWithoutImpl userService = (UserServiceWithoutImpl)enhancer.create();
        userService.login("mary", "789");
    }
}

結(jié)果:

歡迎mary登錄!
method:login, startTime:1605775483020, responseTime:10

CGLIB 創(chuàng)建動(dòng)態(tài)代理類的模式是:

  • 查找目標(biāo)類上的所有非final 的public類型的方法定義秉馏;
  • 將這些方法的定義轉(zhuǎn)換成字節(jié)碼耙旦;
  • 將組成的字節(jié)碼轉(zhuǎn)換成相應(yīng)的代理的class對(duì)象;
  • 實(shí)現(xiàn) MethodInterceptor接口萝究,用來處理對(duì)代理類上所有方法的請(qǐng)求

JDK 動(dòng)態(tài)代理 和 CGLIB動(dòng)態(tài)代理 的對(duì)比

JDK Proxy CGLIB
代理工廠實(shí)現(xiàn)接口 InvocationHandler MethodInterceptor
動(dòng)態(tài)生成代理類 Proxy Enhancer

JDK動(dòng)態(tài)代理:基于Java反射機(jī)制實(shí)現(xiàn)母廷,必須要實(shí)現(xiàn)了接口的業(yè)務(wù)類才能用這種辦法生成代理對(duì)象。
cglib動(dòng)態(tài)代理:基于ASM機(jī)制實(shí)現(xiàn)糊肤,通過生成業(yè)務(wù)類的子類作為代理類琴昆。

JDK Proxy 的優(yōu)勢(shì):

  • 最小化依賴關(guān)系,減少依賴意味著簡(jiǎn)化開發(fā)和維護(hù)馆揉,JDK 本身的支持业舍,可能比 cglib 更加可靠。
  • 平滑進(jìn)行 JDK 版本升級(jí)升酣,而字節(jié)碼類庫(kù)通常需要進(jìn)行更新以保證在新版 Java 上能夠使用舷暮。
  • 代碼實(shí)現(xiàn)簡(jiǎn)單。

基于 cglib 框架的優(yōu)勢(shì):

  • 無需實(shí)現(xiàn)接口噩茄,達(dá)到代理類無侵入
  • 只操作我們關(guān)心的類下面,而不必為其他相關(guān)類增加工作量。
  • 高性能

以上代碼已上傳至:github

參考:
給女同事講完代理后绩聘,女同事說:你好棒哦
Java 動(dòng)態(tài)代理詳解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沥割,一起剝皮案震驚了整個(gè)濱河市耗啦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌机杜,老刑警劉巖帜讲,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異椒拗,居然都是意外死亡似将,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蚀苛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來在验,“玉大人,你說我怎么就攤上這事堵未∫牒欤” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵兴溜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我耻陕,道長(zhǎng)拙徽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任诗宣,我火速辦了婚禮膘怕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘召庞。我一直安慰自己岛心,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布篮灼。 她就那樣靜靜地躺著忘古,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诅诱。 梳的紋絲不亂的頭發(fā)上髓堪,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音娘荡,去河邊找鬼干旁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛炮沐,可吹牛的內(nèi)容都是我干的争群。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼大年,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼换薄!你這毒婦竟也來了玉雾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤专控,失蹤者是張志新(化名)和其女友劉穎抹凳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伦腐,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赢底,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柏蘑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幸冻。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咳焚,靈堂內(nèi)的尸體忽然破棺而出洽损,到底是詐尸還是另有隱情,我是刑警寧澤革半,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布碑定,位于F島的核電站,受9級(jí)特大地震影響又官,放射性物質(zhì)發(fā)生泄漏延刘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一六敬、第九天 我趴在偏房一處隱蔽的房頂上張望碘赖。 院中可真熱鬧,春花似錦外构、人聲如沸普泡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撼班。三九已至,卻和暖如春垒酬,著一層夾襖步出監(jiān)牢的瞬間权烧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工伤溉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留般码,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓乱顾,卻偏偏與公主長(zhǎng)得像板祝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子走净,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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