【設(shè)計(jì)模式自習(xí)室】詳解代理模式

前言

《設(shè)計(jì)模式自習(xí)室》系列,顧名思義,本系列文章帶你溫習(xí)常見的設(shè)計(jì)模式薛夜。主要內(nèi)容有:

  • 該模式的介紹,包括:
    • 引子版述、意圖(大白話解釋)
    • 類圖梯澜、時(shí)序圖(理論規(guī)范)
  • 該模式的代碼示例:熟悉該模式的代碼長(zhǎng)什么樣子
  • 該模式的優(yōu)缺點(diǎn):模式不是萬(wàn)金油,不可以濫用模式
  • 該模式的應(yīng)用案例:了解它在哪些重要的源碼中被使用

系列文章回顧

結(jié)構(gòu)型——代理模式 Proxy Pattern

引子

通俗的來(lái)講腊徙,代理模式就是我們生活中常見的中介简十。在某些情況下,一個(gè)客戶不想或者不能直接引用一個(gè)對(duì)象撬腾,此時(shí)可以通過一個(gè)稱之為“代理”的第三者來(lái)實(shí)現(xiàn)間接引用。

為什么要用代理模式

  • 中介隔離作用:在某些情況下恢恼,一個(gè)客戶類不想或者不能直接引用一個(gè)委托對(duì)象民傻,而代理類對(duì)象可以在客戶類和委托對(duì)象之間起到中介的作用,其特征是代理類和委托類實(shí)現(xiàn)相同的接口场斑。
  • 開閉原則漓踢,增加功能真正的業(yè)務(wù)功能還是由委托類來(lái)實(shí)現(xiàn),但是可以在業(yè)務(wù)功能執(zhí)行的前后加入一些公共的服務(wù)漏隐。例如我們想給項(xiàng)目加入緩存喧半、日志這些功能,我們就可以使用代理類來(lái)完成青责,而沒必要打開已經(jīng)封裝好的委托類挺据。

定義

代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象并由代理對(duì)象控制對(duì)原對(duì)象的引用脖隶。

常見的代理區(qū)分為靜態(tài)代理和動(dòng)態(tài)代理:

1. 靜態(tài)代理

在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件扁耐,代理類和真實(shí)主題角色的關(guān)系在運(yùn)行前就確定了。

由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼产阱,在對(duì)其編譯婉称。在程序員運(yùn)行之前,代理類.class文件就已經(jīng)被創(chuàng)建了构蹬。

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

為什么類可以動(dòng)態(tài)的生成王暗?

這就涉及到Java虛擬機(jī)的類加載機(jī)制

Java虛擬機(jī)類加載過程主要分為五個(gè)階段:加載、驗(yàn)證庄敛、準(zhǔn)備俗壹、解析、初始化铐姚。其中加載階段需要完成以下3件事情:

  1. 通過一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流
  2. 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
  3. 在內(nèi)存中生成一個(gè)代表這個(gè)類的 java.lang.Class 對(duì)象策肝,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)訪問入口

由于虛擬機(jī)規(guī)范對(duì)這3點(diǎn)要求并不具體,所以實(shí)際的實(shí)現(xiàn)是非常靈活的隐绵,關(guān)于第1點(diǎn)之众,獲取類的二進(jìn)制字節(jié)流(class字節(jié)碼)就有很多途徑:

  1. 從ZIP包獲取,這是JAR依许、EAR棺禾、WAR等格式的基礎(chǔ)
  2. 從網(wǎng)絡(luò)中獲取,典型的應(yīng)用是 Applet
  3. 運(yùn)行時(shí)計(jì)算生成峭跳,這種場(chǎng)景使用最多的是動(dòng)態(tài)代理技術(shù)膘婶,在 java.lang.reflect.Proxy 類中缺前,就是用了 ProxyGenerator.generateProxyClass 來(lái)為特定接口生成形式為 *$Proxy 的代理類的二進(jìn)制字節(jié)流
  4. 由其它文件生成,典型應(yīng)用是JSP悬襟,即由JSP文件生成對(duì)應(yīng)的Class類
  5. 從數(shù)據(jù)庫(kù)中獲取等等

所以衅码,動(dòng)態(tài)代理就是想辦法,根據(jù)接口或目標(biāo)對(duì)象脊岳,計(jì)算出代理類的字節(jié)碼逝段,然后再加載到JVM中使用。

更多Java類加載機(jī)制可以查看:

Java虛擬機(jī)知識(shí)點(diǎn)快速?gòu)?fù)習(xí)手冊(cè)

動(dòng)態(tài)代理又有兩種典型的實(shí)現(xiàn)方式:JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理

  • 通過實(shí)現(xiàn)接口的方式 -> JDK動(dòng)態(tài)代理
  • 通過繼承類的方式 -> CGLIB動(dòng)態(tài)代理

2.1 JDK反射機(jī)制(接口代理)

  • 是在程序運(yùn)行時(shí)通過反射機(jī)制動(dòng)態(tài)創(chuàng)建的割捅。
  • 為需要攔截的接口生成代理對(duì)象以實(shí)現(xiàn)接口方法攔截功能奶躯。

2.2 CGLIB代理

  • 其原理是通過字節(jié)碼技術(shù)為一個(gè)類創(chuàng)建子類,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用亿驾,順勢(shì)織入橫切邏輯嘹黔。
  • 但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類進(jìn)行代理莫瞬。
  • JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)儡蔓。

類圖

如果看不懂UML類圖,可以先粗略瀏覽下該圖乏悄,想深入了解的話浙值,可以繼續(xù)谷歌,深入學(xué)習(xí):

image

代理模式包含如下角色:

  • Subject(抽象主題角色):定義代理類和真實(shí)主題的公共對(duì)外方法檩小,也是代理類代理真實(shí)主題的方法开呐;
  • RealSubject(真實(shí)主題角色):真正實(shí)現(xiàn)業(yè)務(wù)邏輯的類;
  • Proxy(代理主題角色):用來(lái)代理和封裝真實(shí)主題规求;
image

時(shí)序圖

image

代碼實(shí)現(xiàn)和使用場(chǎng)景

代理模式得到了非常廣泛的應(yīng)用筐付,最常用的便是我們?cè)赟pring中使用的CGlib代理,所以我們將代碼示例和使用場(chǎng)景再次融合在一起來(lái)講解阻肿。

主要分為四個(gè)代碼小Demo瓦戚,分別是:

  • 靜態(tài)代理代碼示例
  • JDK動(dòng)態(tài)代理代碼示例
  • CGLIB底層使用的ASM字節(jié)碼插入技術(shù)代碼示例
  • CGLIB動(dòng)態(tài)代理代碼示例

1. 靜態(tài)代理示例代碼

編寫一個(gè)接口 UserService ,以及該接口的一個(gè)實(shí)現(xiàn)類 UserServiceImpl

public interface UserService {
    public void select();   
    public void update();
}

public class UserServiceImpl implements UserService {  
    public void select() {  
        System.out.println("查詢 selectById");
    }
    public void update() {
        System.out.println("更新 update");
    }
}

我們將通過靜態(tài)代理對(duì) UserServiceImpl 進(jìn)行功能增強(qiáng)丛塌,在調(diào)用 select 和 update 之前記錄一些日志(記錄開始和結(jié)束的時(shí)間點(diǎn))较解。寫一個(gè)代理類 UserServiceProxy,代理類需要實(shí)現(xiàn) UserService

public class UserServiceProxy implements UserService {
    private UserService target; // 被代理的對(duì)象

    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    public void select() {
        before();
        target.select();    // 這里才實(shí)際調(diào)用真實(shí)主題角色的方法
        after();
    }
    public void update() {
        before();
        target.update();    // 這里才實(shí)際調(diào)用真實(shí)主題角色的方法
        after();
    }

    private void before() {     // 在執(zhí)行方法之前執(zhí)行
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    private void after() {      // 在執(zhí)行方法之后執(zhí)行
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

客戶端測(cè)試

public class Client1 {
    public static void main(String[] args) {
        UserService userServiceImpl = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(userServiceImpl);

        proxy.select();
        proxy.update();
    }
}

通過靜態(tài)代理赴邻,我們達(dá)到了功能增強(qiáng)的目的印衔,而且沒有侵入原代碼,這是靜態(tài)代理的一個(gè)優(yōu)點(diǎn)姥敛。

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

JDK動(dòng)態(tài)代理主要涉及兩個(gè)類:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler

編寫一個(gè)調(diào)用邏輯處理器 LogHandler 類奸焙,提供日志增強(qiáng)功能,并實(shí)現(xiàn) InvocationHandler 接口;在 LogHandler 中維護(hù)一個(gè)目標(biāo)對(duì)象与帆,這個(gè)對(duì)象是被代理的對(duì)象(真實(shí)主題角色)了赌;在 invoke 方法中編寫方法調(diào)用的邏輯處理

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

public class LogHandler implements InvocationHandler {
    Object target;  // 被代理的對(duì)象,實(shí)際的方法執(zhí)行者

    public LogHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);  // 調(diào)用 target 的 method 方法
        after();
        return result;  // 返回方法的執(zhí)行結(jié)果
    }
    // 調(diào)用invoke方法之前執(zhí)行
    private void before() {
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    // 調(diào)用invoke方法之后執(zhí)行
    private void after() {
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

編寫客戶端玄糟,獲取動(dòng)態(tài)生成的代理類的對(duì)象須借助 Proxy 類的 newProxyInstance 方法勿她,具體步驟可見代碼和注釋

import proxy.UserService;
import proxy.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client2 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 設(shè)置變量可以保存動(dòng)態(tài)代理類,默認(rèn)名稱以 $Proxy0 格式命名
        // System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 1. 創(chuàng)建被代理的對(duì)象茶凳,UserService接口的實(shí)現(xiàn)類
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        // 2. 獲取對(duì)應(yīng)的 ClassLoader
        ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
        // 3. 獲取所有接口的Class嫂拴,這里的UserServiceImpl只實(shí)現(xiàn)了一個(gè)接口UserService,
        Class[] interfaces = userServiceImpl.getClass().getInterfaces();
        // 4. 創(chuàng)建一個(gè)將傳給代理類的調(diào)用請(qǐng)求處理器贮喧,處理所有的代理對(duì)象上的方法調(diào)用
        //     這里創(chuàng)建的是一個(gè)自定義的日志處理器,須傳入實(shí)際的執(zhí)行對(duì)象 userServiceImpl
        InvocationHandler logHandler = new LogHandler(userServiceImpl);
        /*
           5.根據(jù)上面提供的信息猪狈,創(chuàng)建代理對(duì)象 在這個(gè)過程中箱沦,
               a.JDK會(huì)通過根據(jù)傳入的參數(shù)信息動(dòng)態(tài)地在內(nèi)存中創(chuàng)建和.class 文件等同的字節(jié)碼
               b.然后根據(jù)相應(yīng)的字節(jié)碼轉(zhuǎn)換成對(duì)應(yīng)的class,
               c.然后調(diào)用newInstance()創(chuàng)建代理實(shí)例
         */
        UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
        // 調(diào)用代理的方法
        proxy.select();
        proxy.update();

        // 保存JDK動(dòng)態(tài)代理生成的代理類雇庙,類名保存為 UserServiceProxy
        // ProxyUtils.generateClassFile(userServiceImpl.getClass(), "UserServiceProxy");
    }
}

結(jié)果:

log start time [Thu Dec 20 16:55:19 CST 2018] 
查詢 selectById
log end time [Thu Dec 20 16:55:19 CST 2018] 
log start time [Thu Dec 20 16:55:19 CST 2018] 
更新 update
log end time [Thu Dec 20 16:55:19 CST 2018] 

上方1和2中的示例代碼來(lái)自谓形,文中有詳細(xì)的細(xì)節(jié)分析:

http://laijianfeng.org/2018/12/Java-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E8%AF%A6%E8%A7%A3/

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

CGLIB代理則是通過繼承的方式來(lái)生成代理類。

字節(jié)碼修改示例代碼

首先疆前,我們先通過代碼來(lái)了解一下字節(jié)碼修改技術(shù)是如何實(shí)現(xiàn)的

我們使用ASM字節(jié)碼操作類庫(kù)(cglib底層使用的是ASM)來(lái)給出一段示例代碼寒跳,小伙伴們也可以自己在本地運(yùn)行試試。

ASM 可以直接產(chǎn)生二進(jìn)制 class 文件竹椒,它能被用來(lái)動(dòng)態(tài)生成類或者增強(qiáng)既有類的功能童太。

ASM從類文件中讀入信息后,能夠改變類行為胸完,分析類信息书释,甚至能夠根據(jù)用戶要求生成新類。

ASM相對(duì)于其他類似工具如BCEL赊窥、SERP爆惧、Javassist、CGLIB锨能,它的最大的優(yōu)勢(shì)就在于其性能更高扯再,其jar包僅30K。Hibernate和Spring都使用了cglib代理址遇,而cglib底層使用的是ASM熄阻,可見ASM在各種開源框架都有廣泛的應(yīng)用。

Base類:被修改的類傲隶,該類實(shí)現(xiàn)了每3秒輸出一個(gè)process饺律,用來(lái)模擬正在處理的請(qǐng)求。

package asm;

import java.lang.management.ManagementFactory;

public class Base {
    public static void main(String[] args) {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String s = name.split("@")[0];
        //打印當(dāng)前Pid
        System.out.println("pid:"+s);
        while (true) {
            try {
                Thread.sleep(3000L);
            } catch (Exception e) {
                break;
            }
            process();
        }
    }

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

執(zhí)行字節(jié)碼修改和轉(zhuǎn)換的類:該類中,我們實(shí)現(xiàn)在被修改類前后都輸出start和end語(yǔ)句的方法

public class TestTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        System.out.println("Transforming " + className);
        try {
            ClassPool cp = ClassPool.getDefault();
            CtClass cc = cp.get("asm.Base");
            CtMethod m = cc.getDeclaredMethod("process");
            m.insertBefore("{ System.out.println(\"start\"); }");
            m.insertAfter("{ System.out.println(\"end\"); }");
            return cc.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

接著我們生成字節(jié)碼修改的Jar包

public class TestAgent {
    public static void agentmain(String args, Instrumentation inst) {
        inst.addTransformer(new TestTransformer(), true);
        try {
            inst.retransformClasses(TransformTarget.class);
            System.out.println("Agent Load Done.");
        } catch (Exception e) {
            System.out.println("agent load failed!");
        }
    }
}

我們將生成的agent.jar通過JVM Tool寫入正在執(zhí)行的Base進(jìn)程中复濒〔甭簦可以看出,本來(lái)只是3秒輸出procss的類變成了從前后輸出start和end的類巧颈,類被成功修改了畦木。

image

上面字節(jié)碼修改的Demo代碼我放在了自己的Github倉(cāng)庫(kù)中:

https://github.com/qqxx6661/Java_Practise/tree/master/ASMDemo

CGLib動(dòng)態(tài)代理代碼示例

maven引入CGLIB包,然后編寫一個(gè)UserDao類砸泛,它沒有接口十籍,只有兩個(gè)方法,select() 和 update()

public class UserDao {
    public void select() {
        System.out.println("UserDao 查詢 selectById");
    }
    public void update() {
        System.out.println("UserDao 更新 update");
    }
}

編寫一個(gè) LogInterceptor 唇礁,繼承了 MethodInterceptor勾栗,用于方法的攔截回調(diào)

import java.lang.reflect.Method;
import java.util.Date;

public class LogInterceptor implements MethodInterceptor {
    /**
     * @param object 表示要進(jìn)行增強(qiáng)的對(duì)象
     * @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 object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(object, objects);   // 注意這里是調(diào)用 invokeSuper 而不是 invoke界牡,否則死循環(huán),methodProxy.invokesuper執(zhí)行的是原始類的方法漾抬,method.invoke執(zhí)行的是子類的方法
        after();
        return result;
    }
    private void before() {
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    private void after() {
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

測(cè)試類

import net.sf.cglib.proxy.Enhancer;

public class CglibTest {
    public static void main(String[] args) {
        DaoProxy daoProxy = new DaoProxy(); 
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);  // 設(shè)置超類宿亡,cglib是通過繼承來(lái)實(shí)現(xiàn)的
        enhancer.setCallback(daoProxy);

        Dao dao = (Dao)enhancer.create();   // 創(chuàng)建代理類
        dao.update();
        dao.select();
    }
}

運(yùn)行結(jié)果和上面相同。

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

靜態(tài)代理優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):可以做到在符合開閉原則的情況下對(duì)目標(biāo)對(duì)象進(jìn)行功能擴(kuò)展纳令。
  • 缺點(diǎn):當(dāng)需要代理多個(gè)類的時(shí)候挽荠,由于代理對(duì)象要實(shí)現(xiàn)與目標(biāo)對(duì)象一致的接口,有兩種方式:
    • 只維護(hù)一個(gè)代理類泊碑,由這個(gè)代理類實(shí)現(xiàn)多個(gè)接口坤按,但是這樣就導(dǎo)致代理類過于龐大
    • 新建多個(gè)代理類,每個(gè)目標(biāo)對(duì)象對(duì)應(yīng)一個(gè)代理類馒过,但是這樣會(huì)產(chǎn)生過多的代理類

JDK動(dòng)態(tài)代理優(yōu)缺點(diǎn)

  • 優(yōu)勢(shì):雖然相對(duì)于靜態(tài)代理臭脓,動(dòng)態(tài)代理大大減少了我們的開發(fā)任務(wù),同時(shí)減少了對(duì)業(yè)務(wù)接口的依賴腹忽,降低了耦合度来累。
  • 劣勢(shì):只能對(duì)接口進(jìn)行代理

CGLIB動(dòng)態(tài)代理優(yōu)缺點(diǎn)

CGLIB創(chuàng)建的動(dòng)態(tài)代理對(duì)象比JDK創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能更高,但是CGLIB創(chuàng)建代理對(duì)象時(shí)所花費(fèi)的時(shí)間卻比JDK多得多窘奏。

  • 所以對(duì)于單例的對(duì)象嘹锁,因?yàn)闊o(wú)需頻繁創(chuàng)建對(duì)象,用CGLIB合適着裹,反之使用JDK方式要更為合適一些领猾。
  • 同時(shí)由于CGLib由于是采用動(dòng)態(tài)創(chuàng)建子類的方法,對(duì)于final修飾的方法無(wú)法進(jìn)行代理。

參考

補(bǔ)充

裝飾模式與代理模式的區(qū)別

裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)的添加方法摔竿,然而代理模式關(guān)注于控制對(duì)對(duì)象的訪問面粮。

  • 使用代理模式的時(shí)候,我們常常在一個(gè)代理類中創(chuàng)建一個(gè)對(duì)象的實(shí)例继低。
  • 當(dāng)我們使用裝飾器模式的時(shí)候熬苍,我們通常的做法是將原始對(duì)象作為一個(gè)參數(shù)傳給裝飾者的構(gòu)造器
最后編輯于
?著作權(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)店門冗恨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人味赃,你說(shuō)我怎么就攤上這事掀抹。” “怎么了心俗?”我有些...
    開封第一講書人閱讀 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)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虹统!你這毒婦竟也來(lái)了弓坞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荐虐。三九已至,卻和暖如春丸凭,著一層夾襖步出監(jiān)牢的瞬間福扬,已是汗流浹背腕铸。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(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)容