2021-02-28_SpringBoot源碼學(xué)習(xí)筆記之啟動(dòng)流程分析

SpringBoot源碼學(xué)習(xí)筆記之啟動(dòng)流程分析

1概述

本文基于Spring Boot版本2.1.4(SringBoot最新版本2.4.2奶赔。(參考文檔: https://docs.spring.io/spring-boot/docs/current/api/)

2SpringBoot啟動(dòng)流程分析

2.1分析入口

@SpringBootApplication
public class DemoApplication {

   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }

}
// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //1.選擇ApplicationContentType容器類型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //2.初始化,得到 ApplicationContextInitializer.class的springFactories
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   //3.設(shè)置監(jiān)聽器 
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //4.設(shè)置主類(有main的那個(gè)類) 
   this.mainApplicationClass = deduceMainApplicationClass();
}

2.2容器選擇deduceFromClasspath

/**
 * An enumeration of possible types of web application.
 *
 * @author Andy Wilkinson
 * @author Brian Clozel
 * @since 2.0.0
 */
public enum WebApplicationType {

   /**
    * The application should not run as a web application and should not start an
    * embedded web server.
    */
   NONE,

   /**
    * The application should run as a servlet-based web application and should start an
    * embedded servlet web server.
    */
   SERVLET,

   /**
    * The application should run as a reactive web application and should start an
    * embedded reactive web server.
    */
   REACTIVE;

2.2setInitializers(ApplicationContextInitializer)

0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"

2.3setListeners(ApplicationListener)

0 = "org.springframework.boot.ClearCachesApplicationListener"
1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
2 = "org.springframework.boot.context.FileEncodingApplicationListener"
3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
4 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
5 = "org.springframework.boot.context.config.DelegatingApplicationListener"
6 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
7 = "org.springframework.boot.context.logging.LoggingApplicationListener"
8 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
9 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"

2.5run

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   // 1.獲取 spring:SpringApplicationRunListener 
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      // 2.這里涉及容器的創(chuàng)建象浑、刷新等,類似于Spring
      // org.springframework.boot.web.servlet.context.
      //AnnotationConfigServletWebServerApplicationContext@25ce9dc4, started on Thu Jan 01 08:00:00 CST 1970 
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

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

3源碼細(xì)節(jié)

3.1getSpringFactoriesInstances

// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
//根據(jù) type和 classLoader得到springFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   // 1.根據(jù) ApplicationContextInitializer 
   Set<String> names = new LinkedHashSet<>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   // 2.springFactories實(shí)例化
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   // 3.排序 
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
   // 1.得到 org.springframework.context.ApplicationContextInitializer:ConfigurableApplicationContext
   String factoryClassName = factoryClass.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
   // 0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
   // 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
   // 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
   // 3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
   // 4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
   // 5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java

    /**
     * The location to look for factories.
     * <p>Can be present in multiple JAR files.
     */
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
   // todo,cache數(shù)據(jù)來(lái)源 
   // 啟動(dòng)的時(shí)候會(huì)掃描所有jar包下META-INF/spring.factories這個(gè)文件痊夭。第二段代碼的意思是將這些掃描到的文件轉(zhuǎn)成Properties
   // 對(duì)象吞鸭,后面兩個(gè)核心代碼的意思就是說(shuō)將加載到的Properties對(duì)象放入到緩存中
    
   MultiValueMap<String, String> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   try {
      // 加載路徑       
      Enumeration<URL> urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryClassName = ((String) entry.getKey()).trim();
            for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               result.add(factoryClassName, factoryName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

3.2啟動(dòng)類上的注解

/**
 * Indicates a {@link Configuration configuration} class that declares one or more
 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
 * annotation that is equivalent to declaring {@code @Configuration},
 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
 *
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @since 1.2.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.@SpringBootConfiguration這個(gè)注解說(shuō)明再點(diǎn)進(jìn)去查看詳情發(fā)現(xiàn)就是一個(gè)@Configuration注解扩所,這說(shuō)明啟動(dòng)類就是一個(gè)配置類。支持Spring以JavaConfig的形式啟動(dòng)焰盗。
@SpringBootConfiguration
//3.springboot的核心注解
@EnableAutoConfiguration
//2.組件掃描的意思,即默認(rèn)掃描當(dāng)前package以及其子包下面的spring的注解咒林,例如:@Controller熬拒、@Service、@Component等等注解垫竞。
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM,
            classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   @AliasFor(annotation = EnableAutoConfiguration.class)
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   @AliasFor(annotation = EnableAutoConfiguration.class)
   String[] excludeName() default {};

   /**
    * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
    * for a type-safe alternative to String-based package names.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};

   /**
    * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
    * scan for annotated components. The package of each class specified will be scanned.
    * <p>
    * Consider creating a special no-op marker class or interface in each package that
    * serves no purpose other than being referenced by this attribute.
    * @return base packages to scan
    * @since 1.3.0
    */
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

}

3.2.1SpringBootConfiguration

3.2.2ComponentScan

3.2.3EnableAutoConfiguration

該注解是springboot的核心注解,本質(zhì)都是利用了spring framework的 import功能澎粟。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.
@AutoConfigurationPackage
//2.
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

   /**
    * Exclude specific auto-configuration classes such that they will never be applied.
    * @return the classes to exclude
    */
   Class<?>[] exclude() default {};

   /**
    * Exclude specific auto-configuration class names such that they will never be
    * applied.
    * @return the class names to exclude
    * @since 1.3.0
    */
   String[] excludeName() default {};

}

3.2.3.1AutoConfigurationPackage

自動(dòng)配置包,最終依賴的確實(shí)@Import這個(gè)注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 后置處理器,將BeanDef注入到ioc容器
// 導(dǎo)入類型為:Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

3.2.3.2AutoConfigurationImportSelector

自動(dòng)引入組件

/**
 * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
 * auto-configuration}. This class can also be subclassed if a custom variant of
 * {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Stephane Nicoll
 * @author Madhura Bhave
 * @since 1.3.0
 * @see EnableAutoConfiguration
 */
public class AutoConfigurationImportSelector
      implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
      BeanFactoryAware, EnvironmentAware, Ordered {

   private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

   private static final String[] NO_IMPORTS = {};

   private static final Log logger = LogFactory
         .getLog(AutoConfigurationImportSelector.class);

   private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

   private ConfigurableListableBeanFactory beanFactory;

   private Environment environment;

   private ClassLoader beanClassLoader;

   private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
         autoConfigurationMetadata, annotationMetadata);
   return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
final class AutoConfigurationMetadataLoader {

   protected static final String PATH = "META-INF/"
         + "spring-autoconfigure-metadata.properties";

   private AutoConfigurationMetadataLoader() {
   }

   public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
      return loadMetadata(classLoader, PATH);
   }
// spring-boot-autoconfigure-2.1.4.RELEASE-sources.jar!/
// org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
// 執(zhí)行時(shí)機(jī):refresh(AbstractApplicationContext.java):invokeBeanFactoryPostProcessors(beanFactory);
                
/*
spring-boot-autoconfigure\2.1.4.RELEASE\spring-boot-autoconfigure-2.1.4.RELEASE.jar!\
META-INF\spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
*/  

/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param autoConfigurationMetadata the auto-configuration metadata
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(
      AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 這就是一種自定義SPI的實(shí)現(xiàn)方式的功能
    // 獲取所有jar中類型為:AutoConfiguration.class的子類,例如:
    // 0 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
    // 1 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
    // 2 = "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration"
   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 new AutoConfigurationEntry(configurations, exclusions);
}
/**
 * Return the auto-configuration class names that should be considered. By default
 * this method will load candidates using {@link SpringFactoriesLoader} with
 * {@link #getSpringFactoriesLoaderFactoryClass()}.
 * @param metadata the source metadata
 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 * attributes}
 * @return a list of candidate configurations
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
      AnnotationAttributes attributes) {
   // 加載路徑:META-INF/spring.factories 
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
         getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
   Assert.notEmpty(configurations,
         "No auto configuration classes found in META-INF/spring.factories. If you "
               + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class})
// 條件注解,加載SpringApplicationAdminJmxAutoConfiguration的bean條件
@ConditionalOnProperty(
    prefix = "spring.application.admin",
    value = {"enabled"},
    havingValue = "true",
    matchIfMissing = false
)
public class SpringApplicationAdminJmxAutoConfiguration {
@Configuration
@ConditionalOnClass({ MBeanExporter.class })
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true",
      matchIfMissing = true)
public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware {

4總結(jié)

  1. SpringBoot在啟動(dòng)的時(shí)候,首先會(huì)構(gòu)造SpringApplication對(duì)象,獲取并設(shè)置Initializers和Listeners,得到main啟動(dòng)類欢瞪。

  2. 接著調(diào)用run()方法活烙,run()方法執(zhí)行容器刷新,

    2.1.刷新容器的時(shí)候遣鼓,執(zhí)行到容器的后置處理器invokeBeanFactoryPostProcessors方法時(shí),會(huì)去解析啟動(dòng)類上的@SpringBootApplication復(fù)合注解啸盏。

    2.2.這個(gè)注解中有一個(gè)@EnableAutoConfiguration注解,表示開啟自動(dòng)配置功能(實(shí)際是import :AutoConfigurationPackages.Registrar.class)

    2.3.另外還有個(gè)@Import注解引入了一個(gè)AutoConfigurationImportSelector這個(gè)類(它去掃描我們所有jar包下的META-INF下的spring.factories 自動(dòng)配置類文件骑祟,而從這個(gè)配置文件中取找key為EnableAutoConfiguration類的全路徑的值,并加載到容器)

  3. 然后在根據(jù)這些配置類中的條件注解回懦,來(lái)判斷是否將這些配置類及關(guān)聯(lián)的Bean在容器中進(jìn)行實(shí)例化,

  4. (判斷的條件:主要是判斷項(xiàng)目是否有相關(guān)jar包或工程是否引入了相關(guān)的bean)次企。

參考

1.springboot啟動(dòng)時(shí)的一個(gè)自動(dòng)裝配過(guò)程

http://www.reibang.com/p/c50b58b03c60

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粉怕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子抒巢,更是在濱河造成了極大的恐慌贫贝,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛉谜,死亡現(xiàn)場(chǎng)離奇詭異稚晚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)型诚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門客燕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人狰贯,你說(shuō)我怎么就攤上這事也搓。” “怎么了涵紊?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵傍妒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我摸柄,道長(zhǎng)颤练,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任驱负,我火速辦了婚禮嗦玖,結(jié)果婚禮上患雇,老公的妹妹穿的比我還像新娘。我一直安慰自己宇挫,他們只是感情好苛吱,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著器瘪,像睡著了一般翠储。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娱局,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天彰亥,我揣著相機(jī)與錄音咧七,去河邊找鬼衰齐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛继阻,可吹牛的內(nèi)容都是我干的耻涛。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼瘟檩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抹缕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起墨辛,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤卓研,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后睹簇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奏赘,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年太惠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磨淌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凿渊,死狀恐怖梁只,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情埃脏,我是刑警寧澤搪锣,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站彩掐,受9級(jí)特大地震影響淤翔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佩谷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一旁壮、第九天 我趴在偏房一處隱蔽的房頂上張望监嗜。 院中可真熱鬧,春花似錦抡谐、人聲如沸裁奇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)刽肠。三九已至,卻和暖如春免胃,著一層夾襖步出監(jiān)牢的瞬間音五,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工羔沙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躺涝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓扼雏,卻偏偏與公主長(zhǎng)得像坚嗜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诗充,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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