【啃啊啃 Spring5 源碼】細(xì)碎三:AOP兩種動(dòng)態(tài)代理的區(qū)別

spring 中 AOP是基于 “動(dòng)態(tài)代理” 實(shí)現(xiàn),其采用了兩種方式:

  1. java代理:采用java內(nèi)置的代理API實(shí)現(xiàn)
  2. cglib代理:采用第三方API實(shí)現(xiàn)

本文主要闡述這兩種方式的區(qū)別

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

  • 靜態(tài)代理:編譯時(shí)將增強(qiáng)代碼植入class文件,因?yàn)槭蔷幾g期進(jìn)行的增強(qiáng),所以代碼運(yùn)行時(shí)效率比動(dòng)態(tài)代理高。使用Aspect可以實(shí)現(xiàn)靜態(tài)代理苟翻。
  • 動(dòng)態(tài)代理:運(yùn)行時(shí)生成代理類并加載,效率比靜態(tài)代理要低,spring中使用了上文中的兩種動(dòng)態(tài)代理的方式來實(shí)現(xiàn)代理類的生成发侵。

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

cglib代理類的實(shí)現(xiàn)效率是比 jdk代理類實(shí)現(xiàn)效率要高,并且更強(qiáng)大的妆偏,但spring中更推薦使用java原生的代理方式刃鳄,原因在于,第三方lib的實(shí)現(xiàn)有太多不確定性钱骂,使用java原生的代理方式更加有保障和穩(wěn)定叔锐。

java代理類的實(shí)現(xiàn)主要靠Proxy類和InvocationHandler接口來實(shí)現(xiàn)。下面直接拋出示例代碼demo:

/**
 * jdk 代理的示例demo
 */
public class MyJdkProxy{

    /**
     * 接口
     */
    public interface IHello{
        void sayHello();
    }

    /**
     * IHello接口實(shí)現(xiàn)類
     */
    static class Hello implements IHello{
        public void sayHello() {
            System.out.println("Hello world!!");
        }
    }

    /**
     * 自定義InvocationHandler
     */
    static  class HWInvocationHandler implements InvocationHandler {
        //目標(biāo)對(duì)象
        private Object target;
        public HWInvocationHandler(Object target){
            this.target = target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------插入前置通知代碼-------------");
            //執(zhí)行相應(yīng)的目標(biāo)方法
            Object rs = method.invoke(target,args);
            System.out.println("------插入后置處理代碼-------------");
            return rs;
        }
    }
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        /*
         * 方式一:
         */
        //獲取動(dòng)態(tài)代理類class并加載
        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
        //獲得代理類的構(gòu)造函數(shù)见秽,并傳入?yún)?shù)類型InvocationHandler.class
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        //通過構(gòu)造函數(shù)來創(chuàng)建動(dòng)態(tài)代理對(duì)象掌腰,將自定義的InvocationHandler實(shí)例傳入
        IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));

        /*
         * 方式二:可看成方式一的語法糖
         */
//        IHello  iHello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加載接口的類加載器
//                new Class[]{IHello.class},      //一組接口
//                new HWInvocationHandler(new Hello())); //自定義的InvocationHandler


        //通過代理對(duì)象調(diào)用目標(biāo)方法
        iHello.sayHello();
    }
}

代理類的class是在Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class)這個(gè)方法執(zhí)行后被生成,class路徑在項(xiàng)目根目錄的com\sun\proxy中:

我們反編譯下這個(gè)代理類class:

public final class $Proxy0
  extends Proxy
  implements MyJdkProxy.IHello
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  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);
    }
  }
  
  public final void sayHello()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  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);
    }
  }
  
  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") });
      m3 = Class.forName("test.wsz.spring.aop.MyJdkProxy$IHello").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
    }
  }
}

從代理類class可以看出张吉,java生成的代理類實(shí)現(xiàn)了 被代理對(duì)象的接口IHello齿梁,同時(shí)繼承了 Proxy

這個(gè)地方解決了我們的一個(gè)疑惑:spring為什么要采用兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式?

原因就在于勺择,JDK的動(dòng)態(tài)代理只能針對(duì)被接口修飾的類创南,因?yàn)榇眍?code>$Proxy0必須繼承Proxy,Java中的類又是單繼承的省核,所以只能規(guī)定委托類Hello必須有實(shí)現(xiàn)接口IHello稿辙,從而使生成的代理類$Proxy0能夠重寫委托了中的方法sayHello()

這里梳理下java中代理類的生成步驟

  1. 通過Proxy.getProxyClass()方法生成代理類的class文件并加載气忠。(具體生成步驟見 源碼中ProxyClassFactory.apply()
  2. 獲取這個(gè)class的構(gòu)造器邻储,傳入代理類的邏輯實(shí)現(xiàn)類InvocationHandler作為構(gòu)造函數(shù)參數(shù),實(shí)例化class獲得代理類對(duì)象旧噪。
  3. 調(diào)用代理類對(duì)象的對(duì)應(yīng)方法

具體源碼就不分析了吨娜,可看 深度剖析JDK動(dòng)態(tài)代理機(jī)制

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

不多說,直接上demo代碼:

/**
 * cglib 代理的示例demo
 */
public class MyCglibProxy {


    /**
     * Hello類
     */
    static class Hello{
        public void sayHello() {
            System.out.println("Hello world!!");
        }
    }

    static class HelloMethodInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("------插入前置通知代碼-------------");
            //執(zhí)行相應(yīng)的目標(biāo)方法
            Object rs = methodProxy.invokeSuper(o,objects);
            System.out.println("------插入后置處理代碼-------------");

            return rs;
        }
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //代理類基礎(chǔ)自 被代理類Hello
        enhancer.setSuperclass(Hello.class);
        //設(shè)置回調(diào)類淘钟,代理邏輯在回調(diào)類中
        enhancer.setCallback(new HelloMethodInterceptor());

        //生成代理類
        Hello hello = (Hello) enhancer.create();
        hello.sayHello();
    }

}

生成的代理類class:

public class MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
  extends MyCglibProxy.Hello
  implements Factory
{
  private boolean CGLIB$BOUND;
  public static Object CGLIB$FACTORY_DATA;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static Object CGLIB$CALLBACK_FILTER;
  private static final Method CGLIB$sayHello$0$Method;
  private static final MethodProxy CGLIB$sayHello$0$Proxy;
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$equals$1$Method;
  private static final MethodProxy CGLIB$equals$1$Proxy;
  private static final Method CGLIB$toString$2$Method;
  private static final MethodProxy CGLIB$toString$2$Proxy;
  private static final Method CGLIB$hashCode$3$Method;
  private static final MethodProxy CGLIB$hashCode$3$Proxy;
  private static final Method CGLIB$clone$4$Method;
  private static final MethodProxy CGLIB$clone$4$Proxy;
  
  /* Error */
  static void CGLIB$STATICHOOK1()
  {
  
  }
  
  final void CGLIB$sayHello$0()
  {
    super.sayHello();
  }
  
  public final void sayHello()
  {
    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.sayHello();
  }
  
  final boolean CGLIB$equals$1(Object paramObject)
  {
    return super.equals(paramObject);
  }
  
  public final boolean equals(Object paramObject)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$1$Method, new Object[] { paramObject }, CGLIB$equals$1$Proxy);
      tmp41_36;
      return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
    }
    return super.equals(paramObject);
  }
  
  final String CGLIB$toString$2()
  {
    return super.toString();
  }
  
  public final String toString()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return (String)tmp17_14.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
    }
    return super.toString();
  }
  
  final int CGLIB$hashCode$3()
  {
    return super.hashCode();
  }
  
  public final int hashCode()
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
    {
      Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
      tmp36_31;
      return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
    }
    return super.hashCode();
  }
  
  final Object CGLIB$clone$4()
    throws CloneNotSupportedException
  {
    return super.clone();
  }
  
  protected final Object clone()
    throws CloneNotSupportedException
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null) {
      return tmp17_14.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
    }
    return super.clone();
  }
  
  /* Error */
  public static MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature arg0)
  {
    // Byte code:
    //   0: aload_0
    //   1: invokevirtual 119   java/lang/Object:toString   ()Ljava/lang/String;
    //   4: dup
    //   5: invokevirtual 120   java/lang/Object:hashCode   ()I
    //   8: lookupswitch    default:+112->120, -508378822:+52->60, 1535311470:+64->72, 1826985398:+76->84, 1913648695:+88->96, 1984935277:+100->108
    //   60: ldc 122
    //   62: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   65: ifeq +56 -> 121
    //   68: getstatic 116  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$clone$4$Proxy   Lnet/sf/cglib/proxy/MethodProxy;
    //   71: areturn
    //   72: ldc 125
    //   74: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   77: ifeq +44 -> 121
    //   80: getstatic 49   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$sayHello$0$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
    //   83: areturn
    //   84: ldc 127
    //   86: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   89: ifeq +32 -> 121
    //   92: getstatic 68   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$equals$1$Proxy  Lnet/sf/cglib/proxy/MethodProxy;
    //   95: areturn
    //   96: ldc -127
    //   98: invokevirtual 123  java/lang/Object:equals (Ljava/lang/Object;)Z
    //   101: ifeq +20 -> 121
    //   104: getstatic 85  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$toString$2$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
    //   107: areturn
    //   108: ldc -125
    //   110: invokevirtual 123 java/lang/Object:equals (Ljava/lang/Object;)Z
    //   113: ifeq +8 -> 121
    //   116: getstatic 98  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$hashCode$3$Proxy    Lnet/sf/cglib/proxy/MethodProxy;
    //   119: areturn
    //   120: pop
    //   121: aconst_null
    //   122: areturn
  }
  
  public MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc()
  {
    CGLIB$BIND_CALLBACKS(this);
  }
  
  public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
  }
  
  public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
  {
    CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
  }
  
  private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
  {
    1716bafc local1716bafc = (1716bafc)paramObject;
    if (!local1716bafc.CGLIB$BOUND)
    {
      local1716bafc.CGLIB$BOUND = true;
      Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
      if (tmp23_20 == null)
      {
        tmp23_20;
        CGLIB$STATIC_CALLBACKS;
      }
      local1716bafc.CGLIB$CALLBACK_0 = (tmp31_28 == null ? tmp31_28 : (MethodInterceptor)((Callback[])tmp23_20)[0]);
    }
  }
  
  public Object newInstance(Callback[] paramArrayOfCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new 1716bafc();
  }
  
  public Object newInstance(Callback paramCallback)
  {
    CGLIB$SET_THREAD_CALLBACKS(new Callback[] { paramCallback });
    CGLIB$SET_THREAD_CALLBACKS(null);
    return new 1716bafc();
  }
  
  /* Error */
  public Object newInstance(Class[] arg1, Object[] arg2, Callback[] arg3)
  {
    // Byte code:
    //   0: aload_3
    //   1: invokestatic 195    test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS    ([Lnet/sf/cglib/proxy/Callback;)V
    //   4: new 2   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc
    //   7: dup
    //   8: aload_1
    //   9: dup
    //   10: arraylength
    //   11: tableswitch    default:+24->35, 0:+17->28
    //   28: pop
    //   29: invokespecial 196  test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:<init>    ()V
    //   32: goto +17 -> 49
    //   35: goto +3 -> 38
    //   38: pop
    //   39: new 202    java/lang/IllegalArgumentException
    //   42: dup
    //   43: ldc -52
    //   45: invokespecial 207  java/lang/IllegalArgumentException:<init>   (Ljava/lang/String;)V
    //   48: athrow
    //   49: aconst_null
    //   50: invokestatic 195   test/wsz/spring/aop/MyCglibProxy$Hello$$EnhancerByCGLIB$$1716bafc:CGLIB$SET_THREAD_CALLBACKS    ([Lnet/sf/cglib/proxy/Callback;)V
    //   53: areturn
  }
  
  public Callback getCallback(int paramInt)
  {
    CGLIB$BIND_CALLBACKS(this);
    switch (paramInt)
    {
    case 0: 
      break;
    }
    return null;
  }
  
  public void setCallback(int paramInt, Callback paramCallback)
  {
    switch (paramInt)
    {
    case 0: 
      this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
      break;
    }
  }
  
  public Callback[] getCallbacks()
  {
    CGLIB$BIND_CALLBACKS(this);
    return new Callback[] { this.CGLIB$CALLBACK_0 };
  }
  
  public void setCallbacks(Callback[] paramArrayOfCallback)
  {
    this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramArrayOfCallback[0]);
  }
  
  static {}
}

從反編譯的class文件可看出宦赠,cglib生成的代理類是直接繼承被代理類Hello,代理的具體邏輯是通過回調(diào)類HelloMethodInterceptor實(shí)現(xiàn)米母。

這里不詳細(xì)分析cglib的源碼勾扭,可看:深入理解CGLIB動(dòng)態(tài)代理機(jī)制

兩種動(dòng)態(tài)代理方式的區(qū)別

  1. cglib底層運(yùn)用了asm這個(gè)非常強(qiáng)大的Java字節(jié)碼生成框架來生成class, 比jdk代理效率要高
  2. jdk代理只能代理被接口修飾的類铁瞒,而cglib沒有這個(gè)限制(原因上面已經(jīng)分析妙色,jdk是動(dòng)態(tài)生成委托類的接口的實(shí)現(xiàn)類,cglib是動(dòng)態(tài)生成委托類的子類)

最后補(bǔ)充下:spring中慧耍,如果bean實(shí)現(xiàn)了接口身辨,則會(huì)使用jdk代理方式,否則采用cglib代理方式蜂绎。也可通過<aop:aspectj-autoproxy proxy-target-class="true"/>配置來強(qiáng)制使用cglib代理方式

?著作權(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)容