SpringBoot啟動(dòng)流程

源碼版本

作者Spring Boot是基于2.4.0贵涵。每個(gè)版本有些變化列肢,讀者盡量和我保持一致,以防源碼有些出入宾茂。

從哪入手瓷马?

相信很多人嘗試讀過(guò)Spring Boot的源碼,但是始終沒(méi)有找到合適的方法跨晴。那是因?yàn)槟銓?duì)Spring Boot的各個(gè)組件欧聘、機(jī)制不是很了解,研究起來(lái)就像大海撈針端盆。

至于從哪入手不是很簡(jiǎn)單的問(wèn)題嗎怀骤,當(dāng)然主啟動(dòng)類了费封,即是標(biāo)注著@SpringBootApplication注解并且有著main()方法的類,如下一段代碼:

@SpringBootApplication
public class AnnotationDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AnnotationDemoApplication.class, args);
    }
}`

話不多說(shuō)蒋伦,DEBUG伺候弓摘,別怕,搞它........

image

源碼如何切分痕届?

SpringApplication中的靜態(tài)run()方法并不是一步完成的韧献,最終執(zhí)行的源碼如下:

//org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  return new SpringApplication(primarySources).run(args);
 }

很顯然分為兩個(gè)步驟,分別是創(chuàng)建SpringApplication和執(zhí)行run()方法爷抓,下面將分為這兩個(gè)部分介紹势决。

如何創(chuàng)建SpringApplication?

創(chuàng)建即是new對(duì)象了蓝撇,DEBUG跟進(jìn)代碼果复,最終執(zhí)行的SpringApplication構(gòu)造方法如下圖:

image

如上圖中標(biāo)注的注釋,創(chuàng)建過(guò)程重用的其實(shí)分為渤昌、虽抄、這三個(gè)階段,下面將會(huì)一一介紹每個(gè)階段做了什么事独柑。

設(shè)置應(yīng)用類型

這個(gè)過(guò)程非常重要迈窟,直接決定了項(xiàng)目的類型,應(yīng)用類型分為三種忌栅,都在WebApplicationType這個(gè)枚舉類中车酣,如下:

  1. NONE:顧名思義,什么都沒(méi)有索绪,正常流程走湖员,不額外的啟動(dòng)web容器,比如Tomcat瑞驱。
  2. SERVLET:基于servlet的web程序娘摔,需要啟動(dòng)內(nèi)嵌的servletweb容器,比如Tomcat唤反。
  3. REACTIVE:基于reactive的web程序凳寺,需要啟動(dòng)內(nèi)嵌reactiveweb容器,作者不是很了解彤侍,不便多說(shuō)肠缨。

判斷的依據(jù)很簡(jiǎn)單,就是加載對(duì)應(yīng)的類拥刻,比如加載了DispatcherServlet等則會(huì)判斷是Servlet的web程序怜瞒。源碼如下:

static WebApplicationType deduceFromClasspath() {
  if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
    && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
   return WebApplicationType.REACTIVE;
  }
  for (String className : SERVLET_INDICATOR_CLASSES) {
   if (!ClassUtils.isPresent(className, null)) {
    return WebApplicationType.NONE;
   }
  }
  return WebApplicationType.SERVLET;
 }

這里我引入了spring-boot-starter-web,肯定是Servlet的web程序。

設(shè)置初始化器(Initializer)

初始化器ApplicationContextInitializer是個(gè)好東西,用于IOC容器刷新之前初始化一些組件吴汪,比如ServletContextApplicationContextInitializer惠窄。

那么如何獲取初始化器呢?跟著上圖中的代碼進(jìn)入漾橙,在SpringApplication中的如下圖中的方法:

image

相對(duì)重要的就是第一步獲取初始化器的名稱了杆融,這個(gè)肯定是全類名了,詳細(xì)源碼肯定在loadFactoryNames()方法中了霜运,跟著源碼進(jìn)入脾歇,最終調(diào)用的是#SpringFactoriesLoader.loadSpringFactories()方法。

loadSpringFactories()方法就不再詳細(xì)解釋了淘捡,其實(shí)就是從類路徑META-INF/spring.factories中加載ApplicationContextInitializer的值藕各。

spring-boot-autoconfigurespring.factories文件中的值如下圖:

image

上圖中的只是一部分初始化器,因?yàn)?code>spring.factories文件不止一個(gè)焦除。

下圖中是我的demo中注入的初始化器激况,現(xiàn)實(shí)項(xiàng)目中并不止這些。

image

這也告訴我們自定義一個(gè)ApplicationContextInitializer只需要實(shí)現(xiàn)接口膘魄,在spring.factories文件中設(shè)置即可乌逐。

設(shè)置監(jiān)聽(tīng)器(Listener)

監(jiān)聽(tīng)器(ApplicationListener)這個(gè)概念在Spring中就已經(jīng)存在,主要用于監(jiān)聽(tīng)特定的事件(ApplicationEvent)创葡,比如IOC容器刷新浙踢、容器關(guān)閉等。

Spring Boot擴(kuò)展了ApplicationEvent構(gòu)建了SpringApplicationEvent這個(gè)抽象類灿渴,主要用于Spring Boot啟動(dòng)過(guò)程中觸發(fā)的事件洛波,比如程序啟動(dòng)中、程序啟動(dòng)完成等骚露。如下圖:

image

監(jiān)聽(tīng)器如何獲确芩辍?從源碼中知道其實(shí)和初始化器(ApplicationContextInitializer)執(zhí)行的是同一個(gè)方法荸百,同樣是從META-INF/spring.factories文件中獲取。

spring-boot-autoconfigurespring.factories文件中的值如下圖:

image

spring.factories文件不止一個(gè)滨攻,同樣監(jiān)聽(tīng)器也不止以上這些够话。

作者demo中注入的一些監(jiān)聽(tīng)器如下圖:

image

總結(jié)

SpringApplication的構(gòu)建都是為了run()方法啟動(dòng)做鋪墊,構(gòu)造方法中總共就有幾行代碼光绕,最重要的部分就是設(shè)置應(yīng)用類型女嘲、設(shè)置初始化器、設(shè)置監(jiān)聽(tīng)器诞帐。

「注意」:初始化器和這里的監(jiān)聽(tīng)器都要放置在spring.factories文件中才能在這一步驟加載欣尼,否則不會(huì)生效,因此此時(shí)IOC容器還未創(chuàng)建,即使將其注入到IOC容器中也是不會(huì)生效的愕鼓。

作者簡(jiǎn)單的畫(huà)了張執(zhí)行流程圖钙态,僅供參考,如下:

image

執(zhí)行run()方法

上面分析了SpringApplication的構(gòu)建過(guò)程菇晃,一切都做好了鋪墊册倒,現(xiàn)在到了啟動(dòng)的過(guò)程了。

作者根據(jù)源碼將啟動(dòng)過(guò)程分為了「8步」磺送,下面將會(huì)一一介紹驻子。

1. 獲取、啟動(dòng)運(yùn)行過(guò)程監(jiān)聽(tīng)器

SpringApplicationRunListener這個(gè)監(jiān)聽(tīng)器和ApplicationListener不同估灿,它是用來(lái)監(jiān)聽(tīng)?wèi)?yīng)用程序啟動(dòng)過(guò)程的崇呵,接口的各個(gè)方法含義如下:

public interface SpringApplicationRunListener {

    // 在run()方法開(kāi)始執(zhí)行時(shí),該方法就立即被調(diào)用馅袁,可用于在初始化最早期時(shí)做一些工作
    void starting();
    // 當(dāng)environment構(gòu)建完成域慷,ApplicationContext創(chuàng)建之前,該方法被調(diào)用
    void environmentPrepared(ConfigurableEnvironment environment);
    // 當(dāng)ApplicationContext構(gòu)建完成時(shí)司顿,該方法被調(diào)用
    void contextPrepared(ConfigurableApplicationContext context);
    // 在ApplicationContext完成加載芒粹,但沒(méi)有被刷新前,該方法被調(diào)用
    void contextLoaded(ConfigurableApplicationContext context);
    // 在ApplicationContext刷新并啟動(dòng)后大溜,CommandLineRunners和ApplicationRunner未被調(diào)用前化漆,該方法被調(diào)用
    void started(ConfigurableApplicationContext context);
    // 在run()方法執(zhí)行完成前該方法被調(diào)用
    void running(ConfigurableApplicationContext context);
    // 當(dāng)應(yīng)用運(yùn)行出錯(cuò)時(shí)該方法被調(diào)用
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

如何獲取運(yùn)行監(jiān)聽(tīng)器?

SpringApplication#run()方法中钦奋,源碼如下:

//從spring.factories中獲取監(jiān)聽(tīng)器
SpringApplicationRunListeners listeners = getRunListeners(args);

跟進(jìn)getRunListeners()方法座云,其實(shí)還是調(diào)用了loadFactoryNames()方法從spring.factories文件中獲取值,如下:

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

最終注入的是EventPublishingRunListener這個(gè)實(shí)現(xiàn)類付材,創(chuàng)建實(shí)例過(guò)程肯定是通過(guò)反射了朦拖,因此我們看看它的構(gòu)造方法,如下圖:

image

這個(gè)運(yùn)行監(jiān)聽(tīng)器內(nèi)部有一個(gè)事件廣播器(SimpleApplicationEventMulticaster)厌衔,主要用來(lái)廣播特定的事件(SpringApplicationEvent)來(lái)觸發(fā)特定的監(jiān)聽(tīng)器ApplicationListener璧帝。

EventPublishingRunListener中的每個(gè)方法用來(lái)觸發(fā)SpringApplicationEvent中的不同子類。

如何啟動(dòng)運(yùn)行監(jiān)聽(tīng)器富寿?

SpringApplication#run()方法中睬隶,源碼如下:

//執(zhí)行starting()方法
listeners.starting(bootstrapContext, this.mainApplicationClass);

執(zhí)行SpringApplicationRunListenersstarting()方法,跟進(jìn)去其實(shí)很簡(jiǎn)單页徐,遍歷執(zhí)行上面獲取的運(yùn)行監(jiān)聽(tīng)器苏潜,這里只有一個(gè)EventPublishingRunListener。因此執(zhí)行的是它的starting()方法变勇,源碼如下圖:

image

上述源碼中邏輯很簡(jiǎn)單恤左,其實(shí)只是執(zhí)行了multicastEvent()方法,廣播了ApplicationStartingEvent事件。至于multicastEvent()內(nèi)部方法感興趣的可以看看飞袋,其實(shí)就是遍歷ApplicationListener的實(shí)現(xiàn)類戳气,找到監(jiān)聽(tīng)ApplicationStartingEvent這個(gè)事件的監(jiān)聽(tīng)器,執(zhí)行onApplicationEvent()方法授嘀。

總結(jié)

這一步其實(shí)就是廣播了ApplicationStartingEvent事件來(lái)觸發(fā)監(jiān)聽(tīng)這個(gè)事件的ApplicationListener物咳。

因此如果自定義了ApplicationListener并且監(jiān)聽(tīng)了ApplicationStartingEvent(應(yīng)用程序開(kāi)始啟動(dòng))事件,則這個(gè)監(jiān)聽(tīng)器將會(huì)被觸發(fā)蹄皱。

2. 環(huán)境構(gòu)建

這一步主要用于加載系統(tǒng)配置以及用戶的自定義配置(application.properties)览闰,源碼如下,在run()方法中:

ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

prepareEnvironment方法內(nèi)部廣播了ApplicationEnvironmentPreparedEvent事件巷折,源碼如下圖:

image

環(huán)境構(gòu)建這一步加載了系統(tǒng)環(huán)境配置压鉴、用戶自定義配置并且廣播了ApplicationEnvironmentPreparedEvent事件,觸發(fā)監(jiān)聽(tīng)器锻拘。

3. 創(chuàng)建IOC容器

源碼在run()方法中油吭,如下:

context = createApplicationContext();

跟進(jìn)代碼,真正執(zhí)行的是ApplicationContextFactory方法署拟,如下圖:

image

根據(jù)webApplicationType決定創(chuàng)建的類型婉宰,很顯然,我這里的是servlet推穷,因此創(chuàng)建的是AnnotationConfigServletWebServerApplicationContext心包。

這一步僅僅是創(chuàng)建了IOC容器,未有其他操作馒铃。

4. IOC容器的前置處理

這一步真是精華了蟹腾,在刷新容器之前做準(zhǔn)備,其中有一個(gè)非常關(guān)鍵的操作:將啟動(dòng)類注入容器区宇,為后續(xù)的自動(dòng)化配置奠定基礎(chǔ)娃殖。源碼如下:

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

prepareContext()源碼解析如下圖,內(nèi)容還是挺多的:

image

從上圖可以看出步驟很多议谷,下面將會(huì)詳細(xì)介紹幾個(gè)重點(diǎn)的內(nèi)容炉爆。

調(diào)用初始化器

SpringApplication構(gòu)建過(guò)程中設(shè)置的初始化器,從spring.factories取值的卧晓。執(zhí)行的流程很簡(jiǎn)單叶洞,遍歷執(zhí)行,源碼如下圖:

image

將自定義的ApplicationContextInitializer放在META-INF/spring.factories中禀崖,在此時(shí)也是會(huì)被調(diào)用。

加載啟動(dòng)類螟炫,注入容器

這一步是將主啟動(dòng)類加載到IOC容器中波附,作為后續(xù)自動(dòng)配置的入口。

SpringApplication構(gòu)建過(guò)程中將主啟動(dòng)類放置在primarySources這個(gè)集合中,此時(shí)的getAllSources()即是從其中取值掸屡,如下圖:

image

這里取出的就是主啟動(dòng)類封寞,當(dāng)然你的項(xiàng)目中可能不止一個(gè),接下來(lái)就是將其加載到IOC容器中了仅财,源碼如下:

load(context, sources.toArray(new Object[0]));

跟著代碼進(jìn)去狈究,其實(shí)主要邏輯都在BeanDefinitionLoader.load()方法,如下圖:

image

將主啟動(dòng)類加載到beanDefinitionMap盏求,后續(xù)該啟動(dòng)類將作為開(kāi)啟自動(dòng)配置化的入口抖锥,后續(xù)章節(jié)詳細(xì)介紹。

兩次廣播事件

這一步涉及到了兩次事件廣播碎罚,分別是ApplicationContextInitializedEventApplicationPreparedEvent磅废,對(duì)應(yīng)的源碼如下:

listeners.contextPrepared(context);
load(context, sources.toArray(new Object[0]));

5. 刷新容器

刷新容器完全是Spring的功能了,比如初始化資源荆烈,初始化上下文廣播器等拯勉,這個(gè)就不再詳細(xì)介紹,有興趣可以看看Spring的源碼憔购。

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    //調(diào)用創(chuàng)建的容器applicationContext中的refresh()方法
    ((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        /**
         * 刷新上下文環(huán)境
         */
        prepareRefresh();

        /**
         * 初始化BeanFactory宫峦,解析XML,相當(dāng)于之前的XmlBeanFactory的操作玫鸟,
         */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        /**
         * 為上下文準(zhǔn)備BeanFactory导绷,即對(duì)BeanFactory的各種功能進(jìn)行填充,如常用的注解@Autowired @Qualifier等
         * 添加ApplicationContextAwareProcessor處理器
         * 在依賴注入忽略實(shí)現(xiàn)*Aware的接口鞋邑,如EnvironmentAware诵次、ApplicationEventPublisherAware等
         * 注冊(cè)依賴,如一個(gè)bean的屬性中含有ApplicationEventPublisher(beanFactory)枚碗,則會(huì)將beanFactory的實(shí)例注入進(jìn)去
         */
        prepareBeanFactory(beanFactory);

        try {
            /**
             * 提供子類覆蓋的額外處理逾一,即子類處理自定義的BeanFactoryPostProcess
             */
            postProcessBeanFactory(beanFactory);

            /**
             * 激活各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
             * 執(zhí)行對(duì)應(yīng)的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
             */
            invokeBeanFactoryPostProcessors(beanFactory);

            /**
             * 注冊(cè)攔截Bean創(chuàng)建的Bean處理器,即注冊(cè)BeanPostProcessor肮雨,不是BeanFactoryPostProcessor遵堵,注意兩者的區(qū)別
             * 注意,這里僅僅是注冊(cè)怨规,并不會(huì)執(zhí)行對(duì)應(yīng)的方法陌宿,將在bean的實(shí)例化時(shí)執(zhí)行對(duì)應(yīng)的方法
             */
            registerBeanPostProcessors(beanFactory);

            /**
             * 初始化上下文中的資源文件,如國(guó)際化文件的處理等
             */
            initMessageSource();

            /**
             * 初始化上下文事件廣播器波丰,并放入applicatioEventMulticaster,如ApplicationEventPublisher
             */
            initApplicationEventMulticaster();

            /**
             * 給子類擴(kuò)展初始化其他Bean
             */
            onRefresh();

            /**
             * 在所有bean中查找listener bean壳坪,然后注冊(cè)到廣播器中
             */
            registerListeners();

            /**
             * 設(shè)置轉(zhuǎn)換器
             * 注冊(cè)一個(gè)默認(rèn)的屬性值解析器
             * 凍結(jié)所有的bean定義,說(shuō)明注冊(cè)的bean定義將不能被修改或進(jìn)一步的處理
             * 初始化剩余的非惰性的bean掰烟,即初始化非延遲加載的bean
             */
            finishBeanFactoryInitialization(beanFactory);

            /**
             * 通過(guò)spring的事件發(fā)布機(jī)制發(fā)布ContextRefreshedEvent事件爽蝴,以保證對(duì)應(yīng)的監(jiān)聽(tīng)器做進(jìn)一步的處理
             * 即對(duì)那種在spring啟動(dòng)后需要處理的一些類沐批,這些類實(shí)現(xiàn)了ApplicationListener<ContextRefreshedEvent>,
             * 這里就是要觸發(fā)這些類的執(zhí)行(執(zhí)行onApplicationEvent方法)
             * 另外蝎亚,spring的內(nèi)置Event有ContextClosedEvent九孩、ContextRefreshedEvent、ContextStartedEvent发框、ContextStoppedEvent躺彬、RequestHandleEvent
             * 完成初始化,通知生命周期處理器lifeCycleProcessor刷新過(guò)程梅惯,同時(shí)發(fā)出ContextRefreshEvent通知其他人
             */
            finishRefresh();
        }

        finally {

            resetCommonCaches();
        }
    }
}

6. IOC容器的后置處理

一個(gè)擴(kuò)展方法宪拥,源碼如下:

afterRefresh(context, applicationArguments);

默認(rèn)為空,如果有自定義需求可以重寫(xiě)个唧,比如打印一些啟動(dòng)結(jié)束日志等江解。

7. 發(fā)出結(jié)束執(zhí)行的事件

同樣是EventPublishingRunListener這個(gè)監(jiān)聽(tīng)器,廣播ApplicationStartedEvent事件徙歼。

但是這里廣播事件和前幾次不同犁河,并不是廣播給SpringApplication中的監(jiān)聽(tīng)器(在構(gòu)建過(guò)程中從spring.factories文件獲取的監(jiān)聽(tīng)器)。因此在IOC容器中注入的監(jiān)聽(tīng)器(使用@Component等方式注入的)也能夠生效魄梯。前面幾個(gè)事件只有在spring.factories文件中設(shè)置的監(jiān)聽(tīng)器才會(huì)生效桨螺。

跟著代碼進(jìn)入,可以看到started()方法源碼如下:

image

這里并沒(méi)有用事件廣播器SimpleApplicationEventMulticaster廣播事件酿秸,而是使用ConfigurableApplicationContext直接在IOC容器中發(fā)布事件豆胸。

8. 執(zhí)行Runners

Spring Boot 提供了兩種Runner讓我們定制一些額外的操作瘾英,分別是CommandLineRunnerApplicationRunner,關(guān)于這兩個(gè)的區(qū)別,后面文章詳細(xì)介紹擅编。

調(diào)用的源碼如下:

callRunners(context, applicationArguments);

跟進(jìn)代碼藏否,其實(shí)真正調(diào)執(zhí)行的是如下方法:

image

邏輯很簡(jiǎn)單期吓,從IOC容器中獲取苛白,遍歷調(diào)用。

總結(jié)

Spring Boot 啟動(dòng)流程相對(duì)簡(jiǎn)單些退客,作者將其細(xì)分了以上八個(gè)步驟骏融,希望能夠幫助讀者理解,流程圖如下:

image

總結(jié)

Spring Boot啟動(dòng)流程就介紹到這里了萌狂,需要重點(diǎn)理解run()方法執(zhí)行的八個(gè)步驟以及事件档玻、初始化器、監(jiān)聽(tīng)器等組件的執(zhí)行時(shí)間點(diǎn)茫藏。

作者:熬夜加班
鏈接:http://www.reibang.com/p/2acb10bab12b
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有误趴。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處务傲。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冤留,一起剝皮案震驚了整個(gè)濱河市碧囊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纤怒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件天通,死亡現(xiàn)場(chǎng)離奇詭異泊窘,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)像寒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門烘豹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人诺祸,你說(shuō)我怎么就攤上這事携悯。” “怎么了筷笨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵憔鬼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我胃夏,道長(zhǎng)轴或,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任仰禀,我火速辦了婚禮照雁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘答恶。我一直安慰自己饺蚊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布悬嗓。 她就那樣靜靜地躺著污呼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烫扼。 梳的紋絲不亂的頭發(fā)上曙求,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音映企,去河邊找鬼悟狱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛堰氓,可吹牛的內(nèi)容都是我干的挤渐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼双絮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浴麻!你這毒婦竟也來(lái)了得问?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤软免,失蹤者是張志新(化名)和其女友劉穎宫纬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體膏萧,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漓骚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榛泛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝌蹂。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖曹锨,靈堂內(nèi)的尸體忽然破棺而出孤个,到底是詐尸還是另有隱情,我是刑警寧澤沛简,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布齐鲤,位于F島的核電站,受9級(jí)特大地震影響覆享,放射性物質(zhì)發(fā)生泄漏佳遂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一撒顿、第九天 我趴在偏房一處隱蔽的房頂上張望丑罪。 院中可真熱鬧,春花似錦凤壁、人聲如沸吩屹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)煤搜。三九已至,卻和暖如春唧席,著一層夾襖步出監(jiān)牢的瞬間擦盾,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工淌哟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迹卢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓徒仓,卻偏偏與公主長(zhǎng)得像腐碱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掉弛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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