SpringBoot啟動(dòng)原理及相關(guān)流程

一、springboot啟動(dòng)原理及相關(guān)流程概覽

springboot是基于spring的新型的輕量級(jí)框架碉钠,最厲害的地方當(dāng)屬自動(dòng)配置甫贯。那我們就可以根據(jù)啟動(dòng)流程和相關(guān)原理來(lái)看看,如何實(shí)現(xiàn)傳奇的自動(dòng)配置


二赏表、springboot的啟動(dòng)類入口

用過(guò)springboot的技術(shù)人員很顯而易見的兩者之間的差別就是視覺(jué)上很直觀的:springboot有自己獨(dú)立的啟動(dòng)類(獨(dú)立程序)

@SpringBootApplication

public?class?Application {

????public?static?void?main(String[] args) {

????????SpringApplication.run(Application.class, args);

????}

}


?從上面代碼可以看出,Annotation定義(@SpringBootApplication)和類定義(SpringApplication.run)最為耀眼,所以要揭開SpringBoot的神秘面紗瓢剿,我們要從這兩位開始就可以了岁诉。

三、單單是SpringBootApplication接口用到了這些注解

@Target(ElementType.TYPE)?// 注解的適用范圍跋选,其中TYPE用于描述類、接口(包括包注解類型)或enum聲明

@Retention(RetentionPolicy.RUNTIME)?// 注解的生命周期哗蜈,保留到class文件中(三個(gè)生命周期)

@Documented?// 表明這個(gè)注解應(yīng)該被javadoc記錄

@Inherited?// 子類可以繼承該注解

@SpringBootConfiguration?// 繼承了Configuration前标,表示當(dāng)前是注解類

@EnableAutoConfiguration?// 開啟springboot的注解功能,springboot的四大神器之一距潘,其借助@import的幫助

@ComponentScan(excludeFilters = {?// 掃描路徑設(shè)置(具體使用待確認(rèn))

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public?@interface?SpringBootApplication {

...

}

在其中比較重要的有三個(gè)注解炼列,分別是:

1)@SpringBootConfiguration // 繼承了Configuration,表示當(dāng)前是注解類

2)@EnableAutoConfiguration // 開啟springboot的注解功能音比,springboot的四大神器之一俭尖,其借助@import的幫助

3)@ComponentScan(excludeFilters = { // 掃描路徑設(shè)置(具體使用待確認(rèn))

?接下來(lái)對(duì)三個(gè)注解一一詳解,增加對(duì)springbootApplication的理解

1)@Configuration注解

按照原來(lái)xml配置文件的形式洞翩,在springboot中我們大多用配置類來(lái)解決配置問(wèn)題

配置bean方式的不同:

a)xml配置文件的形式配置bean

<?xml version="1.0"?encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"

default-lazy-init="true">

<!--bean定義-->

</beans>

b)javaconfiguration的配置形式配置bean

@Configuration

public?class?MockConfiguration{

????//bean定義

}

注入bean方式的不同:

a)xml配置文件的形式注入bean

<bean id="mockService"?class="..MockServiceImpl">

...

</bean>

b)javaconfiguration的配置形式注入bean

@Configuration

public?class?MockConfiguration{

????@Bean

????public?MockService mockService(){

????????return?new?MockServiceImpl();

????}

}

?任何一個(gè)標(biāo)注了@Bean的方法稽犁,其返回值將作為一個(gè)bean定義注冊(cè)到Spring的IoC容器,方法名將默認(rèn)成該bean定義的id骚亿。

表達(dá)bean之間依賴關(guān)系的不同:

a)xml配置文件的形式表達(dá)依賴關(guān)系

<bean id="mockService"?class="..MockServiceImpl">

<propery name ="dependencyService"?ref="dependencyService"?/>

</bean>

<bean id="dependencyService"?class="DependencyServiceImpl"></bean>

b)javaconfiguration配置的形式表達(dá)依賴關(guān)系

@Configuration

public?class?MockConfiguration{

@Bean

public?MockService mockService(){

???? return?new?MockServiceImpl(dependencyService());

}

@Bean

public?DependencyService dependencyService(){

???? return?new?DependencyServiceImpl();

}

}

如果一個(gè)bean的定義依賴其他bean,則直接調(diào)用對(duì)應(yīng)的JavaConfig類中依賴bean的創(chuàng)建方法就可以了已亥。

2)?@ComponentScan注解

作用:a)對(duì)應(yīng)xml配置中的元素;

b)?ComponentScan的功能其實(shí)就是自動(dòng)掃描并加載符合條件的組件(比如@Component和@Repository等)或者bean定義;

c)?將這些bean定義加載到IoC容器中.

我們可以通過(guò)basePackages等屬性來(lái)細(xì)粒度的定制@ComponentScan自動(dòng)掃描的范圍来屠,如果不指定虑椎,則默認(rèn)Spring框架實(shí)現(xiàn)會(huì)從聲明@ComponentScan所在類的package進(jìn)行掃描。

1

注:所以SpringBoot的啟動(dòng)類最好是放在root?package下俱笛,因?yàn)槟J(rèn)不指定basePackages捆姜。

3)?@EnableAutoConfiguration

此注解顧名思義是可以自動(dòng)配置,所以應(yīng)該是springboot中最為重要的注解迎膜。

在spring框架中就提供了各種以@Enable開頭的注解泥技,例如:?@EnableScheduling、@EnableCaching星虹、@EnableMBeanExport等零抬;?@EnableAutoConfiguration的理念和做事方式其實(shí)一脈相承簡(jiǎn)單概括一下就是,借助@Import的支持宽涌,收集和注冊(cè)特定場(chǎng)景相關(guān)的bean定義平夜。

@EnableScheduling是通過(guò)@Import將Spring調(diào)度框架相關(guān)的bean定義都加載到IoC容器【定時(shí)任務(wù)、時(shí)間調(diào)度任務(wù)】

@EnableMBeanExport是通過(guò)@Import將JMX相關(guān)的bean定義加載到IoC容器【監(jiān)控JVM運(yùn)行時(shí)狀態(tài)】

?@EnableAutoConfiguration也是借助@Import的幫助卸亮,將所有符合自動(dòng)配置條件的bean定義加載到IoC容器忽妒。

@EnableAutoConfiguration作為一個(gè)復(fù)合Annotation,其自身定義關(guān)鍵信息如下:

@SuppressWarnings("deprecation")

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage【重點(diǎn)注解】

@Import(EnableAutoConfigurationImportSelector.class)【重點(diǎn)注解】

public?@interface?EnableAutoConfiguration {

...

}

其中最重要的兩個(gè)注解已經(jīng)標(biāo)注:1、@AutoConfigurationPackage【重點(diǎn)注解】2、@Import(EnableAutoConfigurationImportSelector.class)【重點(diǎn)注解】

??當(dāng)然還有其中比較重要的一個(gè)類就是:EnableAutoConfigurationImportSelector.class

AutoConfigurationPackage注解:

static?class?Registrar?implements?ImportBeanDefinitionRegistrar, DeterminableImports {


@Override

public?void?registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {

register(registry,?new?PackageImport(metadata).getPackageName());

}

它其實(shí)是注冊(cè)了一個(gè)Bean的定義段直。

new PackageImport(metadata).getPackageName()吃溅,它其實(shí)返回了當(dāng)前主程序類的?同級(jí)以及子級(jí)????的包組件。

以上圖為例鸯檬,DemoApplication是和demo包同級(jí)决侈,但是demo2這個(gè)類是DemoApplication的父級(jí),和example包同級(jí)

也就是說(shuō)喧务,DemoApplication啟動(dòng)加載的Bean中赖歌,并不會(huì)加載demo2,這也就是為什么功茴,我們要把DemoApplication放在項(xiàng)目的最高級(jí)中庐冯。

Import(AutoConfigurationImportSelector.class)注解:

可以從圖中看出??AutoConfigurationImportSelector?繼承了?DeferredImportSelector?繼承了?ImportSelector

ImportSelector有一個(gè)方法為:selectImports。

@Override

public?String[] selectImports(AnnotationMetadata annotationMetadata) {

if?(!isEnabled(annotationMetadata)) {

return?NO_IMPORTS;

}

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = getAttributes(annotationMetadata);

List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);

configurations = removeDuplicates(configurations);

Set<String> exclusions = getExclusions(annotationMetadata, attributes);

checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = filter(configurations, autoConfigurationMetadata);

fireAutoConfigurationImportEvents(configurations, exclusions);

return?StringUtils.toStringArray(configurations);

}

可以看到第九行坎穿,它其實(shí)是去加載??public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件展父。這個(gè)外部文件,有很多自動(dòng)配置的類玲昧。如下:

其中栖茉,最關(guān)鍵的要屬@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector孵延,@EnableAutoConfiguration可以幫助SpringBoot應(yīng)用將所有符合條件的@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建并使用的IoC容器衡载。就像一只“八爪魚”一樣。



自動(dòng)配置幕后英雄:SpringFactoriesLoader詳解

借助于Spring框架原有的一個(gè)工具類:SpringFactoriesLoader的支持隙袁,@EnableAutoConfiguration可以智能的自動(dòng)配置功效才得以大功告成痰娱!

SpringFactoriesLoader屬于Spring框架私有的一種擴(kuò)展方案,其主要功能就是從指定的配置文件META-INF/spring.factories加載配置菩收。


public?abstract?class?SpringFactoriesLoader {

//...

public?static?<T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {

...

}



public?static?List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

....

}

}

配合@EnableAutoConfiguration使用的話梨睁,它更多是提供一種配置查找的功能支持,即根據(jù)@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查找的Key,獲取對(duì)應(yīng)的一組@Configuration類

上圖就是從SpringBoot的autoconfigure依賴包中的META-INF/spring.factories配置文件中摘錄的一段內(nèi)容娜饵,可以很好地說(shuō)明問(wèn)題坡贺。

所以,@EnableAutoConfiguration自動(dòng)配置的魔法騎士就變成了:從classpath中搜尋所有的META-INF/spring.factories配置文件箱舞,并將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對(duì)應(yīng)的配置項(xiàng)通過(guò)反射(Java Refletion)實(shí)例化為對(duì)應(yīng)的標(biāo)注了@Configuration的JavaConfig形式的IoC容器配置類遍坟,然后匯總為一個(gè)并加載到IoC容器。

?四晴股、springboot啟動(dòng)流程概覽圖


深入探索SpringApplication執(zhí)行流程

SpringApplication的run方法的實(shí)現(xiàn)是我們本次旅程的主要線路愿伴,該方法的主要流程大體可以歸納如下:

1) 如果我們使用的是SpringApplication的靜態(tài)run方法,那么电湘,這個(gè)方法里面首先要?jiǎng)?chuàng)建一個(gè)SpringApplication對(duì)象實(shí)例隔节,然后調(diào)用這個(gè)創(chuàng)建好的SpringApplication的實(shí)例方法鹅经。在SpringApplication實(shí)例初始化的時(shí)候,它會(huì)提前做幾件事情:

public?static?ConfigurableApplicationContext run(Object[] sources, String[] args) {

????return?new?SpringApplication(sources).run(args);

}

根據(jù)classpath里面是否存在某個(gè)特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來(lái)決定是否應(yīng)該創(chuàng)建一個(gè)為Web應(yīng)用使用的ApplicationContext類型怎诫。

使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationContextInitializer瘾晃。

使用SpringFactoriesLoader在應(yīng)用的classpath中查找并加載所有可用的ApplicationListener。

推斷并設(shè)置main方法的定義類幻妓。??

@SuppressWarnings({?"unchecked",?"rawtypes"?})

private?void?initialize(Object[] sources) {

if?(sources !=?null?&& sources.length >?0) {

this.sources.addAll(Arrays.asList(sources));

}

this.webEnvironment = deduceWebEnvironment();

setInitializers((Collection) getSpringFactoriesInstances(

ApplicationContextInitializer.class));

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

this.mainApplicationClass = deduceMainApplicationClass();

}

2) SpringApplication實(shí)例初始化完成并且完成設(shè)置后蹦误,就開始執(zhí)行run方法的邏輯了,方法執(zhí)行伊始肉津,首先遍歷執(zhí)行所有通過(guò)SpringFactoriesLoader可以查找到并加載的SpringApplicationRunListener胖缤。調(diào)用它們的started()方法,告訴這些SpringApplicationRunListener阀圾,“嘿,SpringBoot應(yīng)用要開始執(zhí)行咯狗唉!”初烘。

public?ConfigurableApplicationContext run(String... args) {

StopWatch stopWatch =?new?StopWatch();

stopWatch.start();

ConfigurableApplicationContext context =?null;

FailureAnalyzers analyzers =?null;

configureHeadlessProperty();

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

try?{

ApplicationArguments applicationArguments =?new?DefaultApplicationArguments(

args);

ConfigurableEnvironment environment = prepareEnvironment(listeners,

applicationArguments);

Banner printedBanner = printBanner(environment);

context = createApplicationContext();

analyzers =?new?FailureAnalyzers(context);

prepareContext(context, environment, listeners, applicationArguments,

printedBanner);

// 核心點(diǎn):會(huì)打印springboot的啟動(dòng)標(biāo)志,直到server.port端口啟動(dòng)

refreshContext(context);

afterRefresh(context, applicationArguments);

listeners.finished(context,?null);

stopWatch.stop();

if?(this.logStartupInfo) {

new?StartupInfoLogger(this.mainApplicationClass)

.logStarted(getApplicationLog(), stopWatch);

}

return?context;

}

catch?(Throwable ex) {

handleRunFailure(context, listeners, analyzers, ex);

throw?new?IllegalStateException(ex);

}

}

3) 創(chuàng)建并配置當(dāng)前Spring Boot應(yīng)用將要使用的Environment(包括配置要使用的PropertySource以及Profile)分俯。

private?ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {

// Create and configure the environment

ConfigurableEnvironment environment = getOrCreateEnvironment();

configureEnvironment(environment, applicationArguments.getSourceArgs());

listeners.environmentPrepared(environment);

if?(!this.webEnvironment) {

environment =?new?EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);

}

return?environment;

}

?4) 遍歷調(diào)用所有SpringApplicationRunListener的environmentPrepared()的方法肾筐,告訴他們:“當(dāng)前SpringBoot應(yīng)用使用的Environment準(zhǔn)備好了咯!”缸剪。

public?void?environmentPrepared(ConfigurableEnvironment environment) {

for?(SpringApplicationRunListener listener :?this.listeners) {

listener.environmentPrepared(environment);

}

}

5) 如果SpringApplication的showBanner屬性被設(shè)置為true吗铐,則打印banner。


private?Banner printBanner(ConfigurableEnvironment environment) {

if?(this.bannerMode == Banner.Mode.OFF) {

return?null;

}

ResourceLoader resourceLoader =?this.resourceLoader !=?null???this.resourceLoader:?new?DefaultResourceLoader(getClassLoader());

SpringApplicationBannerPrinter bannerPrinter =?new?SpringApplicationBannerPrinter(resourceLoader,?this.banner);

if?(this.bannerMode == Mode.LOG) {

return?bannerPrinter.print(environment,?this.mainApplicationClass, logger);

}

return?bannerPrinter.print(environment,?this.mainApplicationClass, System.out);

}

?6) 根據(jù)用戶是否明確設(shè)置了applicationContextClass類型以及初始化階段的推斷結(jié)果杏节,決定該為當(dāng)前SpringBoot應(yīng)用創(chuàng)建什么類型的ApplicationContext并創(chuàng)建完成唬渗,然后根據(jù)條件決定是否添加ShutdownHook,決定是否使用自定義的BeanNameGenerator奋渔,決定是否使用自定義的ResourceLoader镊逝,當(dāng)然,最重要的嫉鲸,將之前準(zhǔn)備好的Environment設(shè)置給創(chuàng)建好的ApplicationContext使用撑蒜。

7) ApplicationContext創(chuàng)建好之后,SpringApplication會(huì)再次借助Spring-FactoriesLoader玄渗,查找并加載classpath中所有可用的ApplicationContext-Initializer座菠,然后遍歷調(diào)用這些ApplicationContextInitializer的initialize(applicationContext)方法來(lái)對(duì)已經(jīng)創(chuàng)建好的ApplicationContext進(jìn)行進(jìn)一步的處理。

@SuppressWarnings({?"rawtypes",?"unchecked"?})

protected?void?applyInitializers(ConfigurableApplicationContext context) {

for?(ApplicationContextInitializer initializer : getInitializers()) {

Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);

Assert.isInstanceOf(requiredType, context,?"Unable to call initializer.");

initializer.initialize(context);

}

}

?8) 遍歷調(diào)用所有SpringApplicationRunListener的contextPrepared()方法藤树。

private?void?prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,<br>ApplicationArguments applicationArguments, Banner printedBanner) {

context.setEnvironment(environment);

postProcessApplicationContext(context);

applyInitializers(context);

listeners.contextPrepared(context);

if?(this.logStartupInfo) {

logStartupInfo(context.getParent() ==?null);

logStartupProfileInfo(context);

}

// Add boot specific singleton beans

context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);

if?(printedBanner !=?null) {

context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);

}

// Load the sources

Set<Object> sources = getSources();

Assert.notEmpty(sources,?"Sources must not be empty");

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

listeners.contextLoaded(context);

}

?9)最核心的一步浴滴,將之前通過(guò)@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置加載到已經(jīng)準(zhǔn)備完畢的ApplicationContext。

private?void?prepareAnalyzer(ConfigurableApplicationContext context,FailureAnalyzer analyzer) {

if?(analyzer?instanceof?BeanFactoryAware) {

((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());

}

}

?10) 遍歷調(diào)用所有SpringApplicationRunListener的contextLoaded()方法岁钓。

public?void?contextLoaded(ConfigurableApplicationContext context) {

for?(SpringApplicationRunListener listener :?this.listeners) {

listener.contextLoaded(context);

}

}

11)?調(diào)用ApplicationContext的refresh()方法巡莹,完成IoC容器可用的最后一道工序司志。

private?void?refreshContext(ConfigurableApplicationContext context) {

refresh(context);

if?(this.registerShutdownHook) {

try?{

context.registerShutdownHook();

}catch?(AccessControlException ex) {

// Not allowed in some environments.

}

}

}

12) 查找當(dāng)前ApplicationContext中是否注冊(cè)有CommandLineRunner,如果有降宅,則遍歷執(zhí)行它們骂远。

private?void?callRunners(ApplicationContext context, ApplicationArguments args) {

List<Object> runners =?new?ArrayList<Object>();

runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());

runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());

AnnotationAwareOrderComparator.sort(runners);

for?(Object runner :?new?LinkedHashSet<Object>(runners)) {

if?(runner?instanceof?ApplicationRunner) {

callRunner((ApplicationRunner) runner, args);

}

if?(runner?instanceof?CommandLineRunner) {

callRunner((CommandLineRunner) runner, args);

}

}

}

13) 正常情況下,遍歷執(zhí)行SpringApplicationRunListener的finished()方法腰根、(如果整個(gè)過(guò)程出現(xiàn)異常激才,則依然調(diào)用所有SpringApplicationRunListener的finished()方法,只不過(guò)這種情況下會(huì)將異常信息一并傳入處理)

去除事件通知點(diǎn)后额嘿,整個(gè)流程如下:

public?void?finished(ConfigurableApplicationContext context, Throwable exception) {

for?(SpringApplicationRunListener listener :?this.listeners) {

callFinishedListener(listener, context, exception);

}

}


總結(jié)

到此瘸恼,SpringBoot的核心組件完成了基本的解析,綜合來(lái)看册养,大部分都是Spring框架背后的一些概念和實(shí)踐方式东帅,SpringBoot只是在這些概念和實(shí)踐上對(duì)特定的場(chǎng)景事先進(jìn)行了固化和升華,而也恰恰是這些固化讓我們開發(fā)基于Sping框架的應(yīng)用更加方便高效球拦。

歡迎工作一到五年的Java工程師朋友們加入Java程序員開發(fā): 721575865

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用靠闭、高并發(fā)、高性能及分布式坎炼、Jvm性能調(diào)優(yōu)愧膀、Spring源碼,MyBatis谣光,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來(lái)學(xué)習(xí)提升自己檩淋,不要再用"沒(méi)有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕萄金,使勁拼蟀悦,給未來(lái)的自己一個(gè)交代!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末氧敢,一起剝皮案震驚了整個(gè)濱河市熬芜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌福稳,老刑警劉巖涎拉,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異的圆,居然都是意外死亡鼓拧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門越妈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)季俩,“玉大人,你說(shuō)我怎么就攤上這事梅掠∽米。” “怎么了店归?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)酪我。 經(jīng)常有香客問(wèn)我消痛,道長(zhǎng),這世上最難降的妖魔是什么都哭? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任秩伞,我火速辦了婚禮,結(jié)果婚禮上欺矫,老公的妹妹穿的比我還像新娘纱新。我一直安慰自己,他們只是感情好穆趴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布脸爱。 她就那樣靜靜地躺著,像睡著了一般未妹。 火紅的嫁衣襯著肌膚如雪簿废。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天教寂,我揣著相機(jī)與錄音,去河邊找鬼执庐。 笑死酪耕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的轨淌。 我是一名探鬼主播迂烁,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼递鹉!你這毒婦竟也來(lái)了盟步?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤躏结,失蹤者是張志新(化名)和其女友劉穎却盘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媳拴,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡黄橘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屈溉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塞关。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖子巾,靈堂內(nèi)的尸體忽然破棺而出帆赢,到底是詐尸還是另有隱情小压,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布椰于,位于F島的核電站怠益,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏廉羔。R本人自食惡果不足惜溉痢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望憋他。 院中可真熱鬧孩饼,春花似錦、人聲如沸竹挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)揪罕。三九已至梯码,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間好啰,已是汗流浹背轩娶。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留框往,地道東北人鳄抒。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像椰弊,于是被迫代替她去往敵國(guó)和親许溅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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