六鳖宾、動態(tài)代理是基于什么原理

編程語言通常有各種不同的分類角度吼砂,動態(tài)類型和靜態(tài)類型就是其中一種分類角度,簡單區(qū)分就是語言類型信息是在運(yùn)行時檢查鼎文,還是在編譯期檢查渔肩。
與其近似的還有一個對比,就是所謂強(qiáng)類型和弱類型拇惋,就是不同類型變量賦值時周偎,是否需要顯式地(強(qiáng)制)進(jìn)行類型轉(zhuǎn)換。
通常認(rèn)為Java是靜態(tài)的強(qiáng)類型語言撑帖,但是因?yàn)樘峁┝祟愃品瓷涞葯C(jī)制蓉坎,也具備了部分動態(tài)類型語言的能力。

一磷仰、反射
反射機(jī)制的使用
反射機(jī)制是Java語言提供的一種基礎(chǔ)功能袍嬉,賦予程序在運(yùn)行時自省(introspect)的能力境蔼,通過反射我們可以直接操作類或者對象灶平,比如獲取某個對象的類定義,獲取類聲明的屬性和方法箍土,調(diào)用方法或者構(gòu)造對象逢享,甚至可以運(yùn)行時動態(tài)修改類定義。
反射提供的AccessibleObject.setAccessible(boolean flag)方法吴藻,它的子類也大都重寫了這個方法瞒爬,這里的所謂accessible可以理解成修飾成員的pulib、protected沟堡、private侧但、這意味著我們可以在運(yùn)行時修改成員訪問限制,入?yún)閠rue時表示反射對象禁止權(quán)限檢查航罗,
入?yún)閒asle時表示開啟反射對象權(quán)限檢查禀横。
setAccessible的應(yīng)用場景非常普遍,遍布我們的開發(fā)粥血、測試柏锄、依賴注入等各種框架中酿箭,比如在O/R Mapping框架中,我們?yōu)橐粋€Java實(shí)體對象運(yùn)行時自動生成setter趾娃、getter的邏輯缭嫡,這是加載或持久化數(shù)據(jù)非常必要的,框架通程疲可以利用反射實(shí)現(xiàn)這個功能而不需要開發(fā)者手動寫類似的代碼妇蛀。
在Java9以后,這個方法的使用可能會存在一些爭議笤成,因?yàn)镴igsaw項(xiàng)目新增的模塊化系統(tǒng)讥耗,出于強(qiáng)封裝性的考慮,對反射訪問進(jìn)行了限制疹启。Jigsaw引入了所謂Opend概念古程,只有當(dāng)被反射的模塊和指定的包對反射調(diào)用者模塊Open,才能使用setAccessible喊崖,否則被認(rèn)為是不合法的操作挣磨,如果我們的實(shí)體類是
定義在模塊里面,我們需要在模塊描述符中明確聲明:

module MyEntities {
    // Open for reflection
    opens com.mycorp to java.persistence;
}

可以使用下面參數(shù)顯式設(shè)置是否需要校驗(yàn)Open權(quán)限:

--illegal-access={ permit | warn | deny }

另外一個典型場景就是繞過API訪問限制荤懂,比如使用反射繞開ArrayList泛型限制:

ArrayList<Integer> integerArrayList = new ArrayList<Integer>();
integerArrayList.add(1);
integerArrayList.add(2);
Method method = integerArrayList.getClass().getMethod("add", Object.class);
method.invoke(integerArrayList, "hello");
System.out.println(JSON.toJSONString(integerArrayList));

反射的常見用法:

public class Apple {

    private int price;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public Apple(int price) {
        this.price = price;
    }

    public Apple() {

    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //正常調(diào)用
        Apple apple = new Apple();
        apple.setPrice(5);
        System.out.println("Apple Price:" + apple.getPrice());

        //使用反射調(diào)用茁裙,無參構(gòu)造器
        Class appleClass = Class.forName("math.foundation.job.java.core.reflect.Apple");
        Constructor constructor = appleClass.getConstructor();
        Object reflectApple = constructor.newInstance();

        Method setMethod = appleClass.getMethod("setPrice", int.class);
        setMethod.invoke(reflectApple, 6);
        Method getMethod = appleClass.getMethod("getPrice");
        System.out.println("reflectApple price:" + getMethod.invoke(reflectApple));

        //有參構(gòu)造器
        Constructor constructor1 = appleClass.getConstructor(int.class);
        Apple apple1 = (Apple) constructor1.newInstance(7);
        System.out.println("有參構(gòu)造器apple:" + JSON.toJSONString(apple1));

        //getFields()可以獲取類的共有屬性,但是無法獲取類的私有屬性.與獲取類屬性一樣节仿,當(dāng)我們?nèi)カ@取類方法晤锥、類構(gòu)造器時,如果要獲取私有方法或私有構(gòu)造器廊宪,則必須使用有 declared 關(guān)鍵字的方法矾瘾。
        Field[] fields = appleClass.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }

        Field[] declaredFields = appleClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getName());
        }
    }
}

反射源碼解析
java.lang.reflect.Method#invoke(Object obj, Object... args)方法表示在指定的對象上使用指定的參數(shù)來調(diào)用指定的方法,個別參數(shù)會自動展開去匹配原始參數(shù)箭启。

  1. 如果底層方法是靜態(tài)方法壕翩,那么第一個入?yún)bj會被忽略,可以為null
  2. 如果底層方法的參數(shù)個數(shù)為0傅寡,args的長度可以為0或者null
  3. 如果底層方法的返回值是原始類型則會被包裝成對象放妈,如果返回值是一個由原始類型組成的數(shù)組則不會進(jìn)行包裝。

invoke方法最終會調(diào)用sun.reflect.MethodAccessor#invoke方法荐操,MethodAccessor是從java.lang.reflect.Method#acquireMethodAccessor方法中獲取到的:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
private MethodAccessor acquireMethodAccessor() {
        // First check to see if one has been created yet, and take it
        // if so
        MethodAccessor tmp = null;
        if (root != null) tmp = root.getMethodAccessor();
        if (tmp != null) {
            methodAccessor = tmp;
        } else {
            // Otherwise fabricate one and propagate it up to the root
            tmp = reflectionFactory.newMethodAccessor(this);
            setMethodAccessor(tmp);
        }

        return tmp;
    }

在這里又會調(diào)用sun.reflect.ReflectionFactory#newMethodAccessor方法:

public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
        if(noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
    }

在這個方法中會有一個判斷條件首先會判斷noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())是否為true芜抒,noInflation為true表示配置了,

sun.reflect.noInflation=true

這個配置表示不開啟類膨脹托启,且當(dāng)class不是匿名類時就直接使用sun.reflect.MethodAccessorGenerator#generateMethod方法來生成一個代理類宅倒,否則就使用sun.reflect.NativeMethodAccessorImpl#invoke方法:

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if(++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }

這個方法中會判斷調(diào)用次數(shù)是否超過了閾值(numInvocations),如果超過該閾值則調(diào)用MethodAccessorGenerator.generateMethod()來生成代理類驾中,否則使用native方式唉堪,閾值可以通過如下參數(shù)來進(jìn)行配置:

sun.reflect.inflationThreshold

這個閾值默認(rèn)為15模聋,源碼注釋上解釋這個JVM所做的一個優(yōu)化,通常初次加載字節(jié)碼實(shí)現(xiàn)反射唠亚,使用Method.invoke() 和 Constructor.newInstance() 加載花費(fèi)的時間是使用原生代碼加載花費(fèi)的3-4倍链方,這使得那些頻繁使用反射的應(yīng)用啟動會花費(fèi)更長的時間。為了避免這種痛苦所以在第一次加載
的時候使用JVM本地實(shí)現(xiàn)灶搜,之后超過閾值才會切換到字節(jié)碼方式實(shí)現(xiàn)祟蚀。

二、動態(tài)代理
動態(tài)代理是一種方便運(yùn)行時動態(tài)構(gòu)建代理割卖,動態(tài)處理代理方法調(diào)用的機(jī)制前酿,很多場景都是利用類似機(jī)制做到的,比如用來包裝RPC調(diào)用鹏溯,面向切面的編程(AOP)罢维。實(shí)現(xiàn)動態(tài)代理的方式很多,比如JDK自身提供的動態(tài)代理丙挽,就是主要利用了上面提到的反射機(jī)制肺孵。還有其他的實(shí)現(xiàn)方式,比如利用傳說中更高性能的字節(jié)碼操作機(jī)制颜阐,類似ASM平窘、cglig(基于ASM)、Javassist等凳怨。通過代理可以讓調(diào)用者和實(shí)現(xiàn)者之間解耦瑰艘,比如RPC調(diào)用,框架內(nèi)部的尋址肤舞、序列化紫新、反序列化等,對于調(diào)用者往往沒有太大意義萨赁,通過代理可以更加優(yōu)雅弊琴,例如:

JDK動態(tài)代理:

public class MyJDKDynamicProxy {
    public static void main(String[] args) {
        HelloImpl helloImpl = new HelloImpl();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(helloImpl);
        Hello hello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), myInvocationHandler);
        hello.sayHello();
    }
}

interface Hello {
    void sayHello();
}

class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello World");
    }
}

class MyInvocationHandler implements InvocationHandler {

    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Invoking sayHello");
        Object result = method.invoke(target, args);
        return result;
    }
}

上面的JDK 動態(tài)代理的例子,非常簡單地實(shí)現(xiàn)了動態(tài)代理的構(gòu)建和代理操作杖爽,從API設(shè)計和實(shí)現(xiàn)的角度,這種實(shí)現(xiàn)仍然具有局限性紫皇,因?yàn)樗且越涌跒橹行牡奈堪玻喈?dāng)于添加了一種對于調(diào)用者沒有太大意義的限制。我們實(shí)例化的是Proxy對象聪铺,而不是真正的被調(diào)用類型化焕,這在實(shí)踐中還是可能帶來各種不便和能力退化。
如果被調(diào)用者沒有實(shí)現(xiàn)接口铃剔,而我們還是希望利用動態(tài)代理機(jī)制撒桨,那么可以考慮其他方式查刻。我們知道Spring AOP支持兩種模式的動態(tài)代理,JDK Proxy或者cglib凤类,如果我們選擇cglib方式就會發(fā)現(xiàn)對接口的依賴被克服了穗泵,cglib動態(tài)代理采用的是創(chuàng)建目標(biāo)類的子類的方式,因?yàn)槭亲宇惢瞻蹋覀兛梢赃_(dá)到近似使用被調(diào)用者本身的效果佃延,但是需要注意的是final方法不能被代理。

JDK動態(tài)代理原理簡單分析:
我們上面的例子中創(chuàng)建動態(tài)代理類的入口為:

Hello hello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), myInvocationHandler);

方法注釋說明這個方法的作用是返回一個指定接口的代理類實(shí)例夷磕,并將方法調(diào)用分派給指定的調(diào)用處理程序履肃。指定接口對應(yīng)的就是我們程序中的Hello,方法調(diào)用指的就是sayHello()方法坐桩,指定的調(diào)用處理程序?qū)?yīng)的是程序中的MyInvocationHandler尺棋。
方法聲明如果開啟securityManager,會去校驗(yàn)getClassLoader绵跷、checkPackageAccess陡鹃、ReflectPermission這三個權(quán)限是否滿足條件。
可以看到真正生成代理類的代碼為:

/*
 * Look up or generate the designated proxy class.
 */
Class<?> cl = getProxyClass0(loader, intfs);

生成代理類之后會通過使用以入?yún)镮nvocationHandler.class類型的構(gòu)造器抖坪,將myInvocationHandler作為入?yún)?chuàng)建一個代理類的實(shí)例萍鲸,這個實(shí)例就是我們程序中使用到的hello對象。下面我們來看一下java.lang.reflect.Proxy#getProxyClass0方法:

/**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

/**
 * 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);
}

這個方法強(qiáng)調(diào)自己的作用是用來生成代理類的擦俐,但是需要確保在調(diào)用自己之前一定要先調(diào)用checkProxyAccess通過權(quán)限驗(yàn)證脊阴,內(nèi)部使用弱引用的緩存來緩存代理類,如果代理類在緩存中存在就直接返回蚯瞧,如果不存在于緩存中就使用ProxyClassFactory來創(chuàng)建代理類嘿期。
WeakCache內(nèi)部實(shí)現(xiàn)了一個弱引用的二級緩存,java.lang.reflect.WeakCache.get(K key, P parameter)方法有兩個參數(shù)埋合,第一個參數(shù)表示一級緩存的key备徐,第二個參數(shù)表示二級緩存的key。
首先會使用

Object cacheKey = CacheKey.valueOf(key, refQueue);

根據(jù)一級緩存的入?yún)ey構(gòu)建一個CacheKey對象作為一級緩存真正的key甚颂,通過使用

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

來構(gòu)建二級緩存的key蜜猾,subKeyFactory實(shí)際上對應(yīng)的是java.lang.reflect.Proxy.KeyFactory:

/**
     * A function that maps an array of interfaces to an optimal key where
     * Class objects representing interfaces are weakly referenced.
     */
    private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
    {
        @Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }
    }

apply方法會根據(jù)被代理的接口生成一個特殊的key,大部分的被代理類都只實(shí)現(xiàn)了一個接口振诬,所以大部分情況都會進(jìn)入

   case 1: return new Key1(interfaces[0]); // the most frequent

分支蹭睡。二級緩存中如果沒有指定的key,會使用循環(huán)獲取的方式初始化一個java.lang.reflect.WeakCache.Factory對象赶么,直到初始化成功為止:

if (supplier == null) {
       supplier = valuesMap.putIfAbsent(subKey, factory);
       if (supplier == null) {
           // successfully installed Factory
           supplier = factory;
       }
       // else retry with winning supplier
   }

初始化成功之后會調(diào)用java.lang.reflect.WeakCache.Factory.get方法獲取代理類肩豁,內(nèi)部會調(diào)用java.lang.reflect.Proxy.ProxyClassFactory.apply來生成、定義給定代理類的Class。
生成的代理類如下:

public final class $proxy4 extends Proxy implements Hello {

   private static Method m1;
   private static Method m3;
   private static Method m2;
   private static Method m0;


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

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

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

從生成代理類可以看到代理類已經(jīng)繼承了Proxy類清钥,實(shí)現(xiàn)了Hello接口琼锋,由于Java語言是單繼承的,所以JDK動態(tài)代理要求必須被代理類必須實(shí)現(xiàn)接口祟昭。
代理類的sayHello方法實(shí)現(xiàn)是:

super.h.invoke(this, m3, (Object[])null);

super.h對應(yīng)的正是MyInvocationHandler缕坎,所以實(shí)際調(diào)用的是MyInvocationHandler.invoke方法。

cglib動態(tài)代理:

public class MyCGLIBDynamicProxy {

    public static void main(String[] args) {
        //在指定目錄下生成代理類从橘,我們可以反編譯看一下里面到底是什么東西
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");

        //使用Enhancer對象念赶,類似于JDK動態(tài)代理的Proxy類
        Enhancer enhancer = new Enhancer();
        //設(shè)置目標(biāo)類的字節(jié)碼文件
        enhancer.setSuperclass(Boy.class);
        //設(shè)置回調(diào)函數(shù)
        enhancer.setCallback(new MyMethodInterceptor());
        //create方法就是正式創(chuàng)建代理類
        Boy boy = (Boy) enhancer.create();
        //調(diào)用代理類的play方法
        boy.play();
        boy.work();
    }
}

class Boy {
    public void play() {
        System.out.println("boy play");
    }

    final public void work() {
        System.out.println("boy work");
    }
}

class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("使用cglib對目標(biāo)類進(jìn)行增強(qiáng)");
        //這里的方法調(diào)用不是使用反射
        Object o1 = methodProxy.invokeSuper(o, objects);
        return o1;
    }
}

除了上面例子中的MethodInterceptor回調(diào)類型之外,cglib還支持一下集中回調(diào)類型:

InvocationHandler(與JDK動態(tài)代理InvocationHandler功能一樣)
NoOp(什么都不會做恰力,被代理類方法正常被執(zhí)行)
LazyLoader(只在第一次調(diào)用的時候會調(diào)用loadObject()方法叉谜,之后調(diào)用不會進(jìn)入loadObject()方法)
Dispatcher(功能與LazyLoader相似,不同的是每次調(diào)用都會進(jìn)入loadObject()方法)
ProxyRefDispatcher(與Dispatcher類似踩萎,不同的是會將代理對象作為入?yún)鬟f進(jìn)來)
FixedValue(調(diào)用代理類每次返回的都是固定的值)

JDK Proxy優(yōu)勢:

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

cglib優(yōu)勢:

  1. 某些調(diào)用目標(biāo)可能不便實(shí)現(xiàn)額外接口企孩,從某種角度看锭碳,限定調(diào)用者實(shí)現(xiàn)接口是有些侵入性的實(shí)踐,類似cglib動態(tài)代理沒有這種限制勿璃。
  2. 只操作我們關(guān)心的類擒抛,不必為其他相關(guān)類增加工作量。
  3. 高性能

動態(tài)代理的應(yīng)用非常廣泛补疑,比如在不同模塊的特定階段做特定的事情歧沪,比如類似日志、用戶鑒權(quán)莲组、全局異常處理诊胞、性能監(jiān)控等。
目前在我們的應(yīng)用中使用了JDK動態(tài)代理進(jìn)行Rpc服務(wù)的容災(zāi)機(jī)房切換锹杈、以及服務(wù)降級的功能撵孤,因?yàn)镽pc服務(wù)天然實(shí)現(xiàn)了接口,使用JDK動態(tài)代理依賴較少嬉橙,維護(hù)相對來說也比較簡單早直。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市市框,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糕韧,老刑警劉巖枫振,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻圃,死亡現(xiàn)場離奇詭異,居然都是意外死亡粪滤,警方通過查閱死者的電腦和手機(jī)斧拍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杖小,“玉大人肆汹,你說我怎么就攤上這事∮枞ǎ” “怎么了昂勉?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扫腺。 經(jīng)常有香客問我岗照,道長,這世上最難降的妖魔是什么笆环? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任攒至,我火速辦了婚禮,結(jié)果婚禮上躁劣,老公的妹妹穿的比我還像新娘迫吐。我一直安慰自己,他們只是感情好账忘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布志膀。 她就那樣靜靜地躺著,像睡著了一般闪萄。 火紅的嫁衣襯著肌膚如雪梧却。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天败去,我揣著相機(jī)與錄音放航,去河邊找鬼。 笑死圆裕,一個胖子當(dāng)著我的面吹牛广鳍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吓妆,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼赊时,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了行拢?” 一聲冷哼從身側(cè)響起祖秒,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竭缝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體房维,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年抬纸,在試婚紗的時候發(fā)現(xiàn)自己被綠了咙俩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡湿故,死狀恐怖阿趁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坛猪,我是刑警寧澤脖阵,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站砚哆,受9級特大地震影響独撇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躁锁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一纷铣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧战转,春花似錦搜立、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刁标,卻和暖如春颠通,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膀懈。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工顿锰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人启搂。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓硼控,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胳赌。 傳聞我的和親對象是個殘疾皇子牢撼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

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