[深入學(xué)習(xí)]Spring Boot中的SpringApplication【轉(zhuǎn)載】

在Spring Boot的入口類中顽耳,我們通常是通過調(diào)用SpringApplication的run方法來啟動(dòng)Spring Boot項(xiàng)目。這節(jié)我們來深入學(xué)習(xí)下SpringApplication的一些細(xì)節(jié)奈梳。

  1. 自定義SpringApplication
    1.1 通過SpringApplication API 調(diào)整
    1.2 通過SpringApplicationBuilder API調(diào)整
  2. SpringApplication準(zhǔn)備階段
    2.1 配置源
    2.2 推斷應(yīng)用類型
    2.3 加載應(yīng)用上下文初始器
    2.4 加載應(yīng)用事件監(jiān)聽器
    2.5 推斷入口類
  3. SpringApplication運(yùn)行階段
    3.1 開啟事件監(jiān)聽
    3.2 開啟運(yùn)行監(jiān)聽器
    3.3 創(chuàng)建Environment
    3.4 是否打印Banner
    3.5 創(chuàng)建Context
    3.6 裝配Context
    3.7 Refresh Context
    3.8 廣播應(yīng)用已開啟
    3.9 執(zhí)行Runner
    3.10 廣播應(yīng)用運(yùn)行中

自定義SpringApplication

默認(rèn)的我們都是直接通過SpringApplication的run方法來直接啟動(dòng)Spring Boot,其實(shí)我們可以通過一些API來調(diào)整某些行為解虱。

通過SpringApplication API調(diào)整

創(chuàng)建一個(gè)SpringBoot項(xiàng)目攘须,添加web 依賴。
然后將入口類代碼修改為:

        SpringApplication application = new SpringApplication(Application.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.setWebApplicationType(WebApplicationType.NONE);
        application.setAdditionalProfiles("dev");
        application.run(args);

通過調(diào)用SpringApplication的方法殴泰,我們關(guān)閉了Banner的打印于宙,設(shè)置應(yīng)用環(huán)境為非WEB應(yīng)用,profiles指定為dev悍汛。除此之外捞魁,SpringApplication還包含了許多別的方法,具體可以查看源碼或者官方文檔:


image.png

通過SpringApplicationBuilder API調(diào)整

SpringApplicationBuilder提供了Fluent API离咐,可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用谱俭,下面的代碼和上面的效果一致,但在編寫上較為方便:

        new SpringApplicationBuilder(Application.class)
                .bannerMode(Banner.Mode.OFF)
                .web(WebApplicationType.NONE)
                .profiles("dev")
                .run(args);

SpringApplication準(zhǔn)備階段

SpringApplication的生命周期階段大致可以分為準(zhǔn)備階段和運(yùn)行階段宵蛀。
我們通過源碼來查看SpringApplication的有參構(gòu)造器:

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

通過有參構(gòu)造器里的代碼我們可以將SpringApplication的準(zhǔn)備階段分為以下幾個(gè)步驟:

配置源

構(gòu)造器中this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));這行代碼用于加載我們配置的Spring Boot Bean源昆著。通常我們使用SpringApplication或者SpringApplicationBuilder的構(gòu)造器來直接指定源。

所謂的Spring Boot Bean源指的是某個(gè)被@SpringBootApplication注解標(biāo)注的類术陶,比如入口類:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
}

我們也可以將上面的代碼改為下面這種方式:

public class Application {

    public static void main(String[] args) {
       SpringApplication application = new SpringApplication(ApplicationResource.class);
        application.run(args);
}
    @SpringBootApplication
    public static class ApplicationResource {

    }
}

這樣也是可行的凑懂。查看SpringApplication的單個(gè)參數(shù)構(gòu)造器:

    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

說明我們除了配置單個(gè)源外,還可以配置多個(gè)源瞳别。

推斷應(yīng)用類型

構(gòu)造器中這行this.webApplicationType = WebApplicationType.deduceFromClasspath();代碼用于推斷當(dāng)前Spring Boot應(yīng)用類型

Spring Boot 2.0后征候,應(yīng)用可以分為下面三種類型:

  1. WebApplicationType.NONE:非WEB類型;
  2. WebApplicationType.REACTIVE:Web Reactive類型祟敛;
  3. WebApplicationType.SERVLET:Web Servlet類型疤坝。
    WebApplicationType.deduceFromClasspath()或根據(jù)當(dāng)前應(yīng)用ClassPath中是否存在相關(guān)的實(shí)現(xiàn)類來判斷應(yīng)用類型到底是哪個(gè),deduceFromClasspath方法的源碼如下所示:
    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }

我們也可以直接通過SpringApplication的setWebApplicationType方法或者SpringApplicationBuilder的web方法來指定當(dāng)前應(yīng)用的類型馆铁。

加載應(yīng)用上下文初始器

接著下一行代碼setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));用于加載應(yīng)用上下文初始器ApplicationContextInitializer跑揉。

getSpringFactoriesInstances方法的源碼如下所示:

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

上面代碼利用Spring工廠加載機(jī)制,實(shí)例化ApplicationContextInitializer實(shí)現(xiàn)類埠巨,并進(jìn)行排序历谍。
所以我們可以通過實(shí)現(xiàn)ApplicationContextInitializer接口用于在Spring Boot應(yīng)用初始化之前執(zhí)行一些自定義操作。

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("ConfigurableApplicationContext.id -" + configurableApplicationContext.getId());
    }
}

上面代碼中實(shí)現(xiàn)了initialize方法辣垒,并且使用@Order注解指定優(yōu)先級望侈。其中Ordered.HIGHEST_PRECEDENCE等于Integer.MIN_VALUE,Ordered.LOWEST_PRECEDENCE等于Integer.MAX_VALUE勋桶。所以數(shù)值越小脱衙,優(yōu)先級越高侥猬。

除了使用@Order注解來指定優(yōu)先級外,我們也可以通過實(shí)現(xiàn)org.springframework.core.Ordered接口的getOrder方法來指定優(yōu)先級捐韩。

public class AfterHelloApplicationContextInitializer implements ApplicationContextInitializer ,Ordered{
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("AfterConfigurableApplicationContext.id -" + configurableApplicationContext.getId());
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

上面通過getOrder方法來指定了優(yōu)先級為最低優(yōu)先級退唠。

創(chuàng)建好后,我們還需在工廠配置文件里配置這兩個(gè)實(shí)現(xiàn)類荤胁。在resources目錄下新建META-INF目錄瞧预,并創(chuàng)建spring.factories文件:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
top.lconcise.config.HelloApplicationContextInitializer,\
top.lconcise.config.AfterHelloApplicationContextInitializer

加載應(yīng)用事件監(jiān)聽器

在加載完應(yīng)用上下文初始器后,下一行的setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));代碼加載了應(yīng)用事件監(jiān)聽器仅政。與加載事件上下文初始器類似垢油,Spring Boot也是通過Spring的工廠方法來實(shí)例化ApplicationListener的實(shí)現(xiàn)類,并進(jìn)行排序已旧。
既然是事件監(jiān)聽秸苗,那么其可以監(jiān)聽什么事件呢?其監(jiān)聽的是ApplicationEvent接口的實(shí)現(xiàn)類运褪,我們查看一下都有哪些事件實(shí)現(xiàn)了這個(gè)接口:

這里我們以ContextClosedEvent為例子來編寫自定義的應(yīng)用事件監(jiān)聽器,監(jiān)聽Spring上下文關(guān)閉事件玖瘸。

@Order(Ordered.HIGHEST_PRECEDENCE)
public class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        System.out.println("ContextClosedEvent: " + contextClosedEvent.getApplicationContext().getId());
    }
}
public class AfterContextClosedEventListener implements ApplicationListener<ContextClosedEvent>, Ordered {
    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        System.out.println("AfterContextClosedEvent: " + contextClosedEvent.getApplicationContext().getId());
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}
# Application Listeners
org.springframework.context.ApplicationListener=\
top.lconcise.listener.ContextClosedEventListener,\
top.lconcise.listener.AfterContextClosedEventListener

推斷入口類

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
            ;
        }

        return null;
    }

SpringApplication運(yùn)行階段

SpringApplication的運(yùn)行階段對應(yīng)SpringApplication的run方法秸讹,我們查看其源碼:

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

運(yùn)行階段大致可以分為下面這幾個(gè)過程:

開啟時(shí)間監(jiān)聽

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

上面代碼用于開啟Spring Boot應(yīng)用啟動(dòng)時(shí)間監(jiān)聽,配合下面的stopWatch.stop();便可以計(jì)算出完整的啟動(dòng)時(shí)間雅倒。

開啟運(yùn)行監(jiān)聽器

        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

getRunListeners方法源碼:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

上面代碼通過SpringFactoriesLoader檢索META-INF/spring.factories找到聲明的所有SpringApplicationRunListener的實(shí)現(xiàn)類并將其實(shí)例化璃诀,然后裝配到List<SpringApplicationRunListener>運(yùn)行監(jiān)聽器集合中。

listeners.started();用于遍歷運(yùn)行監(jiān)聽器集合中的所有SpringApplicationRunListener的實(shí)現(xiàn)類蔑匣,并逐一調(diào)用它們的starting方法劣欢,廣播Spring Boot應(yīng)用要開始啟動(dòng)了。

在Spring Boot中SpringApplicationRunListener接口用于監(jiān)聽整個(gè)Spring Boot應(yīng)用生命周期裁良,其代碼如下所示:

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

創(chuàng)建 Environment

run方法中的這行代碼用于創(chuàng)建并配置當(dāng)前SpringBoot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile):

onfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);

我們已經(jīng)在準(zhǔn)備階段里推斷出了應(yīng)用類型凿将,這里只要根據(jù)相應(yīng)的應(yīng)用類型來創(chuàng)建相應(yīng)的應(yīng)用環(huán)境即可,類型和環(huán)境對應(yīng)關(guān)系如下:

  • Web Reactive: StandardReactiveWebEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment

在prepareEnvironment方法中會(huì)執(zhí)行l(wèi)isteners.environmentPrepared(environment);价脾,用于遍歷調(diào)用所有SpringApplicationRunListener實(shí)現(xiàn)類的environmentPrepared()方法牧抵,廣播Environment準(zhǔn)備完畢。

是否打印Banner

run方法中的這行代碼會(huì)根據(jù)我們的配置來決定是否打印Banner:

Banner printedBanner = this.printBanner(environment);

創(chuàng)建Context

run方法中的這行代碼用于創(chuàng)建ApplicationContext

context = this.createApplicationContext();

不同的環(huán)境對應(yīng)不同的ApplicationContext:

  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext

裝配Context

run方法中的這行代碼用于裝配Context:

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

方法prepareContext的源碼如下所示:

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

prepareContext方法開頭為ApplicationContext加載了environment侨把,之后通過applyInitializers方法逐個(gè)執(zhí)行ApplicationContextInitializer的initialize方法來進(jìn)一步封裝ApplicationContext犀变,并調(diào)用所有的SpringApplicationRunListener實(shí)現(xiàn)類的contextPrepared方法,廣播ApplicationContext已經(jīng)準(zhǔn)備完畢了秋柄。

之后初始化IOC容器获枝,并調(diào)用SpringApplicationRunListener實(shí)現(xiàn)類的contextLoaded方法,廣播ApplicationContext加載完成骇笔,這里就包括通過@EnableAutoConfiguration導(dǎo)入的各種自動(dòng)配置類省店。

Refresh Context

run方法中的這行代碼用于初始化所有自動(dòng)配置類机隙,并調(diào)用ApplicationContext的refresh方法:

this.refreshContext(context);

廣播應(yīng)用已啟動(dòng)

run方法中的這行代碼用于廣播Spring Boot應(yīng)用已啟動(dòng):

listeners.started(context);

started方法會(huì)調(diào)用所有的SpringApplicationRunListener的finished方法,廣播SpringBoot應(yīng)用已經(jīng)成功啟動(dòng)萨西。

執(zhí)行Runner

run方法中的這行代碼callRunners(context, applicationArguments);遍歷所有ApplicationRunner和CommandLineRunner的實(shí)現(xiàn)類有鹿,并執(zhí)行其run方法。我們可以實(shí)現(xiàn)自己的ApplicationRunner或者CommandLineRunner谎脯,來對Spring Boot的啟動(dòng)過程進(jìn)行擴(kuò)展葱跋。

@Component
public class HelloApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner :  Hello SpringBoot ");
    }
}

這里我們需要將HelloApplicationRunner使用@Component注解標(biāo)注,讓其注冊到IOC容器中源梭。

@Component
public class HelloCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner  Hello SpringBoot ");
    }
}

程序運(yùn)行輸出如下:

ApplicationRunner :  Hello SpringBoot 
CommandLineRunner :  Hello SpringBoot 

廣播應(yīng)用運(yùn)行中

run方法中的這行代碼listeners.running(context);用于調(diào)用SpringApplicationRunListener的running方法娱俺,廣播Spring Boot應(yīng)用正在運(yùn)行中。

當(dāng)run方法運(yùn)行出現(xiàn)異常時(shí)废麻,便會(huì)調(diào)用handleRunFailure方法來處理異常荠卷,該方法里會(huì)通過listeners.failed(context, exception);來調(diào)用SpringApplicationRunListener的failed方法,廣播應(yīng)用啟動(dòng)失敗烛愧,并將異常擴(kuò)散出去油宜。

上面所有的廣播事件都是使用Spring的應(yīng)用事件廣播器接口ApplicationEventMulticaster的實(shí)現(xiàn)類SimpleApplicationEventMulticaster來進(jìn)行廣播的。

源碼地址:https://github.com/lbshold/springboot/tree/master/Spring-Boot-SpringApplication

參考文章

https://mrbird.cc/deepin-springboot-application.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怜姿,一起剝皮案震驚了整個(gè)濱河市慎冤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沧卢,老刑警劉巖蚁堤,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異但狭,居然都是意外死亡披诗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門立磁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呈队,“玉大人,你說我怎么就攤上這事息罗〉嘀洌” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵迈喉,是天一觀的道長绍刮。 經(jīng)常有香客問我,道長挨摸,這世上最難降的妖魔是什么孩革? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮得运,結(jié)果婚禮上膝蜈,老公的妹妹穿的比我還像新娘锅移。我一直安慰自己,他們只是感情好饱搏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布非剃。 她就那樣靜靜地躺著,像睡著了一般推沸。 火紅的嫁衣襯著肌膚如雪备绽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天鬓催,我揣著相機(jī)與錄音肺素,去河邊找鬼。 笑死宇驾,一個(gè)胖子當(dāng)著我的面吹牛倍靡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播课舍,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼塌西,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了布卡?” 一聲冷哼從身側(cè)響起雨让,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忿等,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崔挖,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贸街,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸相。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薛匪。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脓鹃,靈堂內(nèi)的尸體忽然破棺而出逸尖,到底是詐尸還是另有隱情,我是刑警寧澤瘸右,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布娇跟,位于F島的核電站,受9級特大地震影響太颤,放射性物質(zhì)發(fā)生泄漏苞俘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一龄章、第九天 我趴在偏房一處隱蔽的房頂上張望吃谣。 院中可真熱鬧乞封,春花似錦、人聲如沸岗憋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仔戈。三九已至关串,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杂穷,已是汗流浹背悍缠。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耐量,地道東北人飞蚓。 一個(gè)月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像廊蜒,于是被迫代替她去往敵國和親趴拧。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359