Spring學(xué)習(xí)筆記:Spring事件及源碼分析

本文僅供學(xué)習(xí)交流使用哄辣,侵權(quán)必刪。
不作商業(yè)用途远豺,轉(zhuǎn)載請(qǐng)注明出處

1. 概述

在Spring Framework中肩祥,ApplicationContext是通過org.springframework.context.ApplicationEventorg.springframework.context.ApplicationListener管理事件飞几。一個(gè)Bean如果實(shí)現(xiàn)了ApplicationListener接口砚哆,這個(gè)Bean就會(huì)監(jiān)聽指定類型的事件,只要這個(gè)指定事件在ApplicationContext中被發(fā)布屑墨,這個(gè)監(jiān)聽器就會(huì)被通知躁锁。這是一種典型的觀察者模式實(shí)現(xiàn)

當(dāng)前使用版本是Spring Framework 5.2.2.RELEASE

1. 1Spring的內(nèi)建事件

事件類型 說明
ContextRefreshedEvent 當(dāng)ApplicationContext初始化完成的時(shí)候會(huì)發(fā)布該事件
ContextStartedEvent 當(dāng)ApplicationContext通過org.springframework.context.ConfigurableApplicationContext#start方法啟動(dòng)會(huì)發(fā)布該事件
ContextStoppedEvent 當(dāng)ApplicationContext通過org.springframework.context.ConfigurableApplicationContext#stop方法停止容器會(huì)發(fā)布該事件
ContextClosedEvent 當(dāng)ApplicationContext通過org.springframework.context.ConfigurableApplicationContext#close方法或者通過JVM的shutdown hook關(guān)閉容器的會(huì)發(fā)布該事件
RequestHandledEvent web環(huán)境事件,當(dāng)一個(gè)http請(qǐng)求完成后發(fā)布該事件卵史。注意這個(gè)事件只有使用了Spring的org.springframework.web.servlet.DispatcherServlet的web程序下才能發(fā)布
ServletRequestHandledEvent RequestHandledEvent的子類战转,擴(kuò)展了一下關(guān)于Servlet上下文信息

除此之外,還能能夠通過繼承org.springframework.context.ApplicationEvent實(shí)現(xiàn)自定義事件以躯。下面將展示如何在Spring環(huán)境中實(shí)現(xiàn)事件的監(jiān)聽以及發(fā)布自定義事件槐秧。

2. 事件監(jiān)聽以及發(fā)布的代碼實(shí)現(xiàn)

org.springframework.context.ApplicationListener監(jiān)聽器注冊(cè)到應(yīng)用上下文的方式有多種:

  1. 可以通過常規(guī)Spring Bean的方式注冊(cè)。
  2. 可以構(gòu)建一個(gè)外部對(duì)象通過ConfigurableApplicationContext#addApplicationListener注冊(cè)到應(yīng)用上下文中忧设。
  3. 另外刁标,從Spring4.2后開始支持以注解的方式注冊(cè)。

并且監(jiān)聽器的回調(diào)方法執(zhí)行有同步和異步兩種方式址晕。下面將展示具體的代碼實(shí)現(xiàn)膀懈。

2.1 通過注冊(cè)Spring Bean的方式注冊(cè)監(jiān)聽器

  • 首先定義一個(gè)監(jiān)聽器實(shí)現(xiàn)類MyApplicationListener,并監(jiān)聽org.springframework.context.event.ContextRefreshedEvent事件谨垃。后面的代碼實(shí)現(xiàn)基本都是繼續(xù)沿用這個(gè)實(shí)現(xiàn)類
   public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
          
       @Override
          public void onApplicationEvent(ContextRefreshedEvent event) {
              System.out.println ("MyApplicationListener#onApplicationEvent event:" + event);
          }
      }
  • 編寫Main方法啟動(dòng)Spring容器并注冊(cè)監(jiān)聽器

    public class SpringEventDemo {
         public static void main(String[] args) {
                     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ();
    
             //注冊(cè)監(jiān)聽器
             applicationContext.register (MyApplicationListener.class);
             
             //啟動(dòng)Spring應(yīng)用上下文
             applicationContext.refresh ();
         
          //關(guān)閉Spring應(yīng)用上下文
             applicationContext.close();
         }
    }
    
  • 這里Main方法執(zhí)行后這個(gè)console會(huì)打印

MyApplicationListener#onApplicationEvent event:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@49c2faae, started on Mon Apr 26 14:17:44 CST 2021]

2.2 通過ConfigurableApplicationContext#addApplicationListener注冊(cè)監(jiān)聽器

  • 編寫Main方法啟動(dòng)Spring容器并注冊(cè)監(jiān)聽器
 public class SpringEventDemo {
      public static void main(String[] args) {
                  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ();
 
          //實(shí)例化監(jiān)聽器對(duì)象
          MyApplicationListener myApplicationListener = new MyApplicationListener ();
          
          //將監(jiān)聽器對(duì)象添加到Spring應(yīng)用上下文
          applicationContext.addApplicationListener (myApplicationListener);
          
          //啟動(dòng)Spring應(yīng)用上下文
          applicationContext.refresh ();
      
       //關(guān)閉Spring應(yīng)用上下文
          applicationContext.close();
      }
 }
  • 這里Main方法執(zhí)行后這個(gè)console會(huì)打印
MyApplicationListener#onApplicationEvent event:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@49c2faae, started on Mon Apr 26 14:17:44 CST 2021]

2.3通過注解方式注冊(cè)&設(shè)置監(jiān)聽器異步回調(diào)

  • 通過@EventListener標(biāo)記在方法上設(shè)置監(jiān)聽器回調(diào)方法
  • 通過@Async和@EnableAsync開啟異步執(zhí)行
    • 開啟異步回調(diào)監(jiān)聽器除了通過注解的方式启搂,還能獲取ApplicationContext的SimpleApplicationEventMulticaster,調(diào)用SimpleApplicationEventMulticaster#setTaskExecutor設(shè)置線程池開啟異步執(zhí)行硼控。但這種方式要注意我們需要自己通過代碼進(jìn)行線程池關(guān)閉,Spring應(yīng)用上下文不會(huì)關(guān)閉我們?cè)O(shè)置進(jìn)去的線程池胳赌。
@EnableAsync
public class AnnotatedAsyncEventListenerDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ();
        applicationContext.register (AnnotatedAsyncEventListenerDemo.class);
        applicationContext.refresh ();
        applicationContext.publishEvent (new MyApplicationEvent ("AnnotatedAsyncEventListenerDemo#MyApplicationEvent"));
        applicationContext.close ();
    }

    @Async
    @EventListener
  public void onListenerEvent(ContextRefreshedEvent contextRefreshedEvent) {
       System.out.printf ("事件執(zhí)行當(dāng)前線程:%s,事件為:%s\n",Thread.currentThread ().getName (),contextRefreshedEvent);

    }
}
  • Main方法執(zhí)行后這個(gè)console會(huì)打印,這里可以看到處理事件的線程并不是main線程
事件執(zhí)行當(dāng)前線程:SimpleAsyncTaskExecutor-1,事件為:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5d099f62, started on Mon Apr 26 17:04:34 CST 2021]

2.4 自定義事件

嘗試自定義一個(gè)事件并進(jìn)行發(fā)布牢撼。

  • 定義自定義事件MyApplicationEvent
 public class MyApplicationEvent extends ApplicationEvent {
     /**
      * Create a new {@code ApplicationEvent}.
      *
      * @param source the object on which the event initially occurred or with
      *               which the event is associated (never {@code null})
      */
     public MyApplicationEvent(String source) {
         super (source);
     }
 
     @Override
     public String getSource() {
         return (String) super.getSource ();
     }
 }
  • 定義監(jiān)聽自定義事件的監(jiān)聽器MyEventListener
    public class MyEventListener implements ApplicationListener<MyApplicationEvent> {
        @Override
        public void onApplicationEvent(MyApplicationEvent event) {
            System.out.printf ("執(zhí)行事件:[%s]\n",event);
        }
    }
    
  • Main方法編寫測(cè)試,這里通過org.springframework.context.ApplicationEventPublisherAware回調(diào)接口發(fā)布事件
  public class CustomizedEventDemo implements ApplicationEventPublisherAware {
  
      public static void main(String[] args) {
          AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ();
          applicationContext.register (CustomizedEventDemo.class);
          applicationContext.addApplicationListener (new MyEventListener ());
          applicationContext.refresh ();
          applicationContext.close ();
      }
  
      @Override
      public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
          applicationEventPublisher.publishEvent (new MyApplicationEvent ("hello,setApplicationEventPublisher"));
      }
  }

2.5 事件異常處理器

在Spring 3.0之后提供了一個(gè)org.springframework.util.ErrorHandler能夠處理事件異常的情況,如圖2-5-1匈织。下面將展示如何向Spring應(yīng)用上下文中注冊(cè)事件異常處理器

圖2-5-1

public class ErrorHandlerDemo {

    public static void main(String[] args) {
        GenericApplicationContext applicationContext = new GenericApplicationContext ();
        applicationContext.addApplicationListener (new MyEventListener ());
        applicationContext.refresh ();
        
        ApplicationEventMulticaster applicationEventMulticaster = applicationContext.getBean (AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
                    (SimpleApplicationEventMulticaster) applicationEventMulticaster;
            /**
             *  Spring事件錯(cuò)誤處理代碼
             *  SimpleApplicationEventMulticaster中設(shè)置ErrorHandler
             *  {@link org.springframework.util.ErrorHandler}
             */
            simpleApplicationEventMulticaster.setErrorHandler (new ErrorHandler () {
                @Override
                public void handleError(Throwable t) {
                    System.err.println ("ErrorHandler處理事件錯(cuò)誤浪默," + t);
                }
            });

            /**
            設(shè)置一個(gè)主動(dòng)拋出異常的監(jiān)聽器
            */
            simpleApplicationEventMulticaster.addApplicationListener (new ApplicationListener<MyApplicationEvent> () {
                @Override
                public void onApplicationEvent(MyApplicationEvent event) {
                    throw new RuntimeException ("主動(dòng)拋出異常,測(cè)試事件錯(cuò)誤處理");
                }
            });

        }
        // 發(fā)布MyApplicationEvent事件觸發(fā)異步事件
        applicationEventMulticaster.multicastEvent (new MyApplicationEvent ("AsyncEventListenerDemo"));
        System.out.println ("關(guān)閉Spring應(yīng)用上下文");
        applicationContext.close ();
    }
}
  • 這里Main方法會(huì)輸出
ErrorHandler處理事件錯(cuò)誤缀匕,java.lang.RuntimeException: 主動(dòng)拋出異常纳决,測(cè)試事件錯(cuò)誤處理

以上就是Spring事件相關(guān)的操作,此外如果我們需要控制監(jiān)聽器的調(diào)用順序乡小,我們可以通過@Order控制具體的執(zhí)行順序阔加。

3.Spring Event源碼分析

3.1 容器啟動(dòng)時(shí),事件的相關(guān)源碼

這里從org.springframework.context.support.AbstractApplicationContext#refresh方法開始入手满钟。首先refresh方法調(diào)用的prepareRefresh方法是容器啟動(dòng)前的準(zhǔn)備胜榔,這里的相關(guān)操作是初始化一個(gè)名為earlyApplicationEventsSet<ApplicationEvent>,這是用于存放早期事件的一個(gè)容器湃番,這個(gè)容器的出現(xiàn)了是為了修復(fù)Spring之前的一個(gè)缺陷夭织,這里切換到Spring Framework 3.0的源碼并嘗試分析這個(gè)缺陷是什么以及到底如何出現(xiàn)的。

這里首先提前說一下吠撮,Spring應(yīng)用上下文擁有事件發(fā)布的能力尊惰,該能力基于其成員屬性org.springframework.context.event.ApplicationEventMulticaster這個(gè)接口的實(shí)現(xiàn)類實(shí)現(xiàn)的

  • 首先先看一下refresh方法的整體流程,如圖3-1-1所示
    1. 首先會(huì)做啟動(dòng)前準(zhǔn)備prepareBeanFactory
    2. 然后調(diào)用invokeBeanFactoryPostProcessor回調(diào)org.springframework.beans.factory.config.BeanFactoryPostProcessor的方法
    3. 再望后執(zhí)行initApplicationEventMulticaster泥兰,這個(gè)方法是實(shí)例化org.springframework.context.event.ApplicationEventMulticaster
    4. 最后調(diào)用registerListeners,將用戶的監(jiān)聽器注冊(cè)到容器中
圖3-1-1
  • 進(jìn)入AbstractApplicationContext#prepareRefresh弄屡,當(dāng)時(shí)并沒有初始化早期事件的容器,如圖3-1-2

    圖3-1-2

  • 導(dǎo)致缺陷發(fā)生的場(chǎng)景如下鞋诗,假設(shè)創(chuàng)建一個(gè)類是實(shí)現(xiàn)了 BeanFactoryPostProcessor以及ApplicationContextAware接口的膀捷。ApplicationContext是有發(fā)布事件的能力,其能力依賴于ApplicationEventMulticaster削彬。當(dāng)我們?cè)谕ㄟ^ApplicationContextAware回調(diào)獲取ApplicaitonContext實(shí)例后全庸,然后在BeanFactoryPostProcessor的回調(diào)接口中利用返回的ApplicationContext實(shí)例發(fā)布實(shí)現(xiàn),Spring應(yīng)用上下文就會(huì)拋出異常融痛,因?yàn)閺纳厦娴恼{(diào)用順序來看糕篇,BeanFactoryPostProcessor的回調(diào)時(shí)間早于ApplicationEventMulticaster的實(shí)例化,這里的ApplicationEventMulticaster還是null的所以會(huì)拋出NPE酌心。下面展示一下異常的示例

  • 定義一個(gè)調(diào)用類BugInvoker

public class BugInvoker implements BeanFactoryPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        applicationContext.publishEvent (new MyApplicationEvent ("hello world"));
    }
}
  • 編寫Main方法測(cè)試
public class Spring3BugDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext ();
        //注冊(cè)到容器中
        applicationContext.register (BugInvoker.class);

        applicationContext.addApplicationListener (new ApplicationListener () {
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                System.out.println (event);
            }
        });

        applicationContext.refresh ();
        applicationContext.close ();
    }
}
  • 這里是啟動(dòng)后拋出的具體異常信息,異常信息告訴用戶ApplicationEventMulticaster還沒有實(shí)例化完成
Exception in thread "main" java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@378fd1ac: startup date [Tue Apr 27 14:46:22 CST 2021]; root of context hierarchy
    at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:307)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:294)
    at com.kgyam.event.springEvent.Spring3BugDemo$BugInvoke.postProcessBeanFactory(Spring3BugDemo.java:48)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:624)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:614)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:398)
    at com.kgyam.event.springEvent.Spring3BugDemo.main(Spring3BugDemo.java:31)

  • 而3.0之后加入的早期事件容器就是為了修復(fù)這個(gè)缺陷拌消,對(duì)早于ApplicationEventMulticaster實(shí)例化前發(fā)布的事件先緩存在這個(gè)容器中,等待ApplicationEventMulticaster實(shí)例化完成后再對(duì)這個(gè)容器里的事件進(jìn)行發(fā)布。

3.2 監(jiān)聽器注冊(cè)

監(jiān)聽器的注冊(cè)比較簡(jiǎn)單,通過org.springframework.context.support.AbstractApplicationContext#addApplicationListener作為入口

  • 將監(jiān)聽器添加到ApplicationEventMulticaster中墩崩,如圖3-2-1


    圖3-2-1
  • org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListener中的添加監(jiān)聽器邏輯是排除掉重復(fù)的監(jiān)聽器以免發(fā)生二次調(diào)用氓英,然后就會(huì)把這個(gè)監(jiān)聽器對(duì)象添加到監(jiān)聽器列表中并清理監(jiān)聽器緩存Map,如圖3-2-2

    圖3-2-2

  • 到這里就基本完成了添加監(jiān)聽器的過程了鹦筹,接下來看下事件發(fā)布相關(guān)源碼铝阐。

3.3事件發(fā)布

事件發(fā)布通過org.springframework.context.support.AbstractApplicationContext#publishEvent作為入口

  • 首先根據(jù)發(fā)布的事件對(duì)象,如果是非ApplicationEvent類型的就會(huì)將其轉(zhuǎn)換為PayloadApplicationEvent對(duì)象铐拐,如圖3-3-1


    圖3-3-1
  • 然后就是如果earlyApplicationEvents不為null就會(huì)將其存放到早期事件容器中徘键,這里是表示applicationEventMulticaster對(duì)象沒有初始化完成,將事件緩存到該早期事件容器中遍蟋。在applicationEventMulticaster初始化完成并發(fā)布所有早期事件容器里面的事件后吹害,會(huì)將該容器設(shè)置為null。
  • 如果applicationEventMulticaster應(yīng)用事件廣播器已經(jīng)實(shí)例化虚青,那么就會(huì)調(diào)用org.springframework.context.event.ApplicationEventMulticaster#multicastEvent發(fā)布事件它呀,如圖3-3-2
    圖3-3-2
  • 往下進(jìn)入ApplicationEventMulticaster的實(shí)現(xiàn)類org.springframework.context.event.SimpleApplicationEventMulticaster看下具體的事件發(fā)布邏輯,如圖3-3-3
    圖3-3-3
  • 調(diào)用getApplicationListeners方法獲取對(duì)應(yīng)事件類型對(duì)應(yīng)的監(jiān)聽器列表棒厘,這其中的邏輯相對(duì)是比較多的纵穿。
  1. 首先會(huì)根據(jù)事件類型和事件源類型生成對(duì)應(yīng)的監(jiān)聽器緩存key對(duì)象org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerCacheKey,根據(jù)這個(gè)key從retrieverCache(Map對(duì)象奢人,如圖3-3-4)獲取監(jiān)聽器列表對(duì)象org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerRetriever,這個(gè)ListenerRetriever是存放了事件類型相關(guān)的監(jiān)聽器列表谓媒,
  2. 如果沒有從緩存中獲取到對(duì)應(yīng)的監(jiān)聽器列表對(duì)象,就從defaultRetriever(存放所有監(jiān)聽器的對(duì)象何乎,圖3-3-5)中找到匹配的監(jiān)聽器生成對(duì)應(yīng)的ListenerRetriever并重新緩存到retrieverCache中篙耗,最后返回監(jiān)聽器列表。


    圖3-3-4

    圖3-3-5
  • 返回監(jiān)聽器列表后就通過invokeListener遍歷回調(diào)監(jiān)聽器宪赶,如圖3-3-6。這里try-catch將捕獲監(jiān)聽器拋出的異常并交給ErrorHandler處理脯燃,如圖3-3-7


    圖3-3-6

    圖3-3-7
  • 到這里事件發(fā)布流程基本就結(jié)束了搂妻。但還有一個(gè)地方需要注意,我們回到org.springframework.context.event.ApplicationEventMulticaster#multicastEvent這里辕棚,如果應(yīng)用上下文存在父子關(guān)系的話欲主,該事件就會(huì)傳遞到父的應(yīng)用上下文中,這種情況下注意出現(xiàn)事件被多次處理的情況逝嚎,如圖3-3-8扁瓢。

    圖3-3-8

3.4 @EventListener注冊(cè)過程

org.springframework.context.event.EventListener的注釋中有提及到該注解是通過一個(gè)內(nèi)建bean對(duì)象org.springframework.context.event.EventListenerMethodProcessor進(jìn)行處理的,如圖3-4-1所示补君。

圖3-4-1

  • 首先先看下EventListenerMethodProcessor類,如圖3-4-2。對(duì)于處理EventListener注解的核心方法是在afterSingletonsInstantiated方法(實(shí)例化完成后處理接口org.springframework.beans.factory.SmartInitializingSingleton的接口方法)荤西。將斷點(diǎn)打在這個(gè)方法上作為入口看下對(duì)應(yīng)的處理邏輯。

    圖3-4-2

  • 該方法首先會(huì)獲取容器中所有的beanName并遍歷敞掘,然后根據(jù)beanName在容器中獲取其對(duì)應(yīng)的Class類型。

  • 獲取到Class類型后找到所有標(biāo)記了注解@EventListener的方法并放到一個(gè)名為annotatedMethods的Map中楣铁,如圖3-4-3玖雁。


    圖3-4-3
  • 如果annotatedMethods非空,就遍歷這個(gè)Map并通過org.springframework.context.event.EventListenerFactory的實(shí)現(xiàn)類對(duì)其進(jìn)行處理盖腕,如圖3-4-5赫冬。這里會(huì)針對(duì)每個(gè)標(biāo)記了注解的Method對(duì)象創(chuàng)建一個(gè)對(duì)應(yīng)的事件監(jiān)聽適配器并通過應(yīng)用上下文的addApplicationContext方法將其添加進(jìn)去。

    圖3-4-5

  • EventListenerFactory在Spring的內(nèi)建實(shí)現(xiàn)只有一個(gè)org.springframework.context.event.DefaultEventListenerFactory溃列,如圖3-4-6劲厌。該接口主要有兩個(gè)方法,第一個(gè)是判斷是否支持該Method對(duì)象哭廉,DefaultEventListenerFactory默認(rèn)返回true脊僚。第二個(gè)方法是針對(duì)該方法創(chuàng)建一個(gè)事件監(jiān)聽器,DefaultEventListenerFactory會(huì)創(chuàng)建一個(gè)事件監(jiān)聽適配器org.springframework.context.event.ApplicationListenerMethodAdapter#ApplicationListenerMethodAdapter遵绰。

    圖3-4-6

  • @EventListener注解的處理基本到這里就結(jié)束了辽幌。

4.總結(jié)

Spring Event使用的是典型的觀察者模型,這種情況能夠讓后監(jiān)聽器更加符合單一職責(zé)原則椿访,并且能夠提高系統(tǒng)的擴(kuò)展性乌企。同時(shí)我們還知道了Spring應(yīng)用上下文ApplicationContext是擁有事件發(fā)布的能力的,其能力是依賴于org.springframework.context.event.ApplicationEventMulticaster成玫。
最后還需要注意的是加酵,如果使用Spring 3.0這種早期版本需要注意事件發(fā)布必須晚于事件廣播器實(shí)例化。

參考文檔

Core Technologies (spring.io)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哭当,一起剝皮案震驚了整個(gè)濱河市猪腕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钦勘,老刑警劉巖陋葡,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異彻采,居然都是意外死亡腐缤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門肛响,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岭粤,“玉大人,你說我怎么就攤上這事特笋√杲剑” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)偿渡。 經(jīng)常有香客問我臼寄,道長(zhǎng),這世上最難降的妖魔是什么溜宽? 我笑而不...
    開封第一講書人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任吉拳,我火速辦了婚禮,結(jié)果婚禮上适揉,老公的妹妹穿的比我還像新娘留攒。我一直安慰自己,他們只是感情好嫉嘀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開白布炼邀。 她就那樣靜靜地躺著,像睡著了一般剪侮。 火紅的嫁衣襯著肌膚如雪拭宁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評(píng)論 1 290
  • 那天瓣俯,我揣著相機(jī)與錄音杰标,去河邊找鬼。 笑死彩匕,一個(gè)胖子當(dāng)著我的面吹牛腔剂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驼仪,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼掸犬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了绪爸?” 一聲冷哼從身側(cè)響起湾碎,我...
    開封第一講書人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奠货,沒想到半個(gè)月后介褥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仇味,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雹顺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹墨。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嬉愧,靈堂內(nèi)的尸體忽然破棺而出贩挣,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布王财,位于F島的核電站卵迂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绒净。R本人自食惡果不足惜见咒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挂疆。 院中可真熱鬧改览,春花似錦、人聲如沸缤言。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胆萧。三九已至庆揩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跌穗,已是汗流浹背订晌。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞻离,地道東北人腾仅。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像套利,于是被迫代替她去往敵國(guó)和親推励。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

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