Spring核心源碼深度解析(七)Spring-Mybatis核心思想

Spring-Mybatis核心思想

經(jīng)過(guò)漫長(zhǎng)的學(xué)習(xí),我們總算對(duì)Spring IOC 和DI的整體流程有了一定的認(rèn)識(shí)崎淳, 可能讀者讀完之后并不覺(jué)得Spring的設(shè)計(jì)有多牛逼削锰,甚至覺(jué)得Spring的設(shè)計(jì)過(guò)于復(fù)雜,那么本章的內(nèi)容將會(huì)讓大家大開眼界官辈,震撼大家如此的想法翘瓮,但前提是對(duì)筆者的前面所提及的內(nèi)容贮折、Spring的主干一定要熟悉。

Spring如何實(shí)現(xiàn)對(duì)外拓展

在Spring源碼的第五章资盅,筆者提到了Import注解调榄,不知大家是否還有印象,如果沒(méi)有印象請(qǐng)讀者自行回顧IOC的知識(shí)呵扛,因?yàn)楣P者這里將重點(diǎn)介紹Spring提供拓展的接口ImportSelector每庆、ImportBeanDefinitionRegistrar、BeanDefinitionRegistryPostProcessor择份,當(dāng)然讀者可能會(huì)提到BeanFactoryPostProcessor扣孟、BeanPostProcessor這兩個(gè)接口烫堤,確實(shí)Spring在內(nèi)部也大量使用了這兩個(gè)接口荣赶,但筆者這里為什么沒(méi)有提出這兩個(gè)接口,我們看下面的實(shí)例

?public class UserBeanPostProcessor implements BeanFactoryPostProcessor,BeanPostProcessor {
?
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    GenericBeanDefinition bd= (GenericBeanDefinition) beanFactory.getBeanDefinition("user1");
    bd.getConstructorArgumentValues().addGenericArgumentValue("1","dsadasdsa");
?
  }
?
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return null;
  }
?
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return null;
  }
}

可以看到我們只能獲取BeanFactory或者BeanName鸽斟,也就是說(shuō)我們只能拿到我們所需要的對(duì)象進(jìn)行修改拔创,而我所提出的ImportSelector、ImportBeanDefinitionRegistrar富蓄、BeanDefinitionRegistryPostProcessor這三個(gè)接口剩燥,他們都可以直接對(duì)工廠添加BeanDefination,我們對(duì)Spring IOC 源碼熟悉的讀者肯定知道立倍,創(chuàng)建了BeanDefination就等于創(chuàng)建了Bean灭红,我們看實(shí)例:

public class
MyImportBeanDefinitionRegistrarimplements ImportBeanDefinitionRegistrar,BeanDefinitionRegistryPostProcessor { 
  @Override  
  public void  registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {   
  } 
  @Override   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throwsBeansException { 
    }  
  @Override   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throwsBeansException {   
  }
 }

可以看到我們通過(guò)實(shí)現(xiàn)這些接口就可以獲取registry,這個(gè)registry能干什么呢口注,那就是注冊(cè)BeanDefination变擒,我們看下圖:


image

,好了現(xiàn)在我們對(duì)Spring拓展Bean的一些重要接口做了簡(jiǎn)要的分析
寝志,現(xiàn)在我們回到我們的主題娇斑。

Spring-Mybatis

首先我們思考一下Mybatis到底干了什么事情策添,無(wú)非就是通過(guò)我們創(chuàng)建的接口進(jìn)行代理,創(chuàng)建代理對(duì)象毫缆,然后通過(guò)代理對(duì)象綁定表字段和對(duì)象屬性唯竹、方法名和SQL語(yǔ)句等,然后執(zhí)行sql語(yǔ)句返回對(duì)象苦丁。了解Mybatis的核心思想后浸颓,Spring-Mybatis就很簡(jiǎn)單了,無(wú)非就是將我們的代理對(duì)象放進(jìn)Spring進(jìn)行管理旺拉,這又回到上文我所提出的Spring如何實(shí)現(xiàn)對(duì)外拓展的知識(shí)了猾愿,那么現(xiàn)在我們已經(jīng)知道先用代理的方式代理通過(guò)接口創(chuàng)建對(duì)象,然后在用Spring擴(kuò)展的技術(shù)將代理對(duì)象放進(jìn)Spring里账阻,是不是就完成了呢蒂秘?好像是這么回事,那么我們開始實(shí)操吧:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
?
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 執(zhí)行包掃描淘太,獲取所需要的Mapper 這一步省略掉
  UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(UserMapper.class.getClassLoader(), new Class[]{UserMapper.class},new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
      }
    });
?
    BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(userMapper.getClass());
    GenericBeanDefinition bd= (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
    registry.registerBeanDefinition("userMapper",bd);
  }
}
image

拋出異常姻僧,可以看到代理類似乎不能直接放入Spring,道理很簡(jiǎn)答蒲牧,我們?cè)倏聪旅娴妮敵?/p>

image

我在代碼里打印了兩種獲取class的方式撇贺,可以看到是不一樣的,因此我們創(chuàng)建BD的時(shí)候傳入的參數(shù)是class com.sun.proxy.$Proxy12冰抢,如果我們改成接口形式的class

image

然后再次執(zhí)行松嘶,可以發(fā)現(xiàn)還是無(wú)法創(chuàng)建

image

因?yàn)槲覀兘唤oSpring的是接口,并沒(méi)有對(duì)該接口進(jìn)行實(shí)現(xiàn)挎扰,因此Spring并不知道如何創(chuàng)建對(duì)象翠订,所以總的來(lái)說(shuō)就是我們不能按照這種思路去將我們的代理對(duì)象交給Spring管理,那Mybatis是如何解決這種問(wèn)題的呢遵倦?不知道讀者時(shí)候了解過(guò)FactoryBean接口尽超,我們點(diǎn)開此接口

public interface FactoryBean<T> {
  @Nullable
  T getObject() throws Exception;
 
  @Nullable
  Class<?> getObjectType();
  
  default boolean isSingleton() {
    return true;
  }
?
}

我們可以看到FactoryBean有三個(gè)方法,返回對(duì)象梧躺、返回類型似谁、是否是單例對(duì)象,我們重點(diǎn)關(guān)注前面兩個(gè)方法FactoryBean提供了返回對(duì)象和類型的方式掠哥,筆者結(jié)合代理的方式簡(jiǎn)單梳理一下巩踏,希望讀者能夠逐漸明白我上文提出的問(wèn)題,以及解決方案

public class MappperFactoryBean implements FactoryBean, InvocationHandler {
?
  private Class clazz;
?
  public MapperFactoryBean(Class clazz){
    this.clazz=clazz;
  }
?
  @Override
  public Object getObject() throws Exception {
    Class[] clazzs=new Class[]{clazz};
    return Proxy.newProxyInstance(this.getClass().getClassLoader(),clazzs,this);
  }
?
  @Override
  public Class<?> getObjectType() {
    return clazz;
  }
?
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Annotation annotation=method.getAnnotation(Select.class);
    System.out.println(((Select) annotation).value());
    return clazz;
  }
}

這里我們創(chuàng)建了一個(gè)實(shí)現(xiàn)了FactoryBean, InvocationHandler的類续搀,然后將 getObject() 改變成返回一個(gè)代理對(duì)象塞琼,getObjectType()返回我們的接口類型,然后我們?cè)趯?duì)MyImportBeanDefinitionRegistrar進(jìn)行改造:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
?
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) throws IllegalAccessException, InstantiationException {
    // 執(zhí)行包掃描目代,獲取所需要的Mapper 這一步省略掉
    BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class);
    GenericBeanDefinition bd= (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
    bd.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
    // 改變自動(dòng)裝配的模式
    /*bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);*/
    registry.registerBeanDefinition("userMapper",bd);
  }
}

在改造測(cè)試類

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext=
        new AnnotationConfigApplicationContext(AppConfig.class);
?
    UserMapper userMapper = (UserMapper) annotationConfigApplicationContext.getBean("userMapper");
    userMapper.selectUserById();
  }

查看執(zhí)行結(jié)果

image

這里我們已經(jīng)執(zhí)行了代理方法屈梁,并獲取了我們注解的內(nèi)容嗤练,這里可能讀者有一些疑問(wèn),為什么我們實(shí)現(xiàn)了FactoryBean就能夠成功在讶,筆者簡(jiǎn)單描述下吧煞抬,如果我們的一個(gè)類實(shí)現(xiàn)了FactoryBean,那么Spring調(diào)用我們的getBean()也就是我們所說(shuō)的依賴查找就不會(huì)走單例對(duì)象的普通路線,而是回調(diào)我們實(shí)現(xiàn)了FactoryBean的getObject() 构哺,getObjectType()方法革答,這樣設(shè)置什么好處呢?個(gè)人認(rèn)為這樣設(shè)置其實(shí)是為了對(duì)象的靈活性曙强,我們的對(duì)象不一定類型和Object是同一種東西残拐,就好比我們的代理對(duì)象,好了多余的就不在描述了碟嘴,我們簡(jiǎn)單看看Spring-Mybatis的源碼部分吧

Spring-Mybatis源碼

在對(duì)筆者上文有所了解后溪食,其實(shí)Spring-Mybatis源碼基本就大差不差了,但有一點(diǎn)或許會(huì)有所區(qū)別娜扇,那就是屬性設(shè)置的問(wèn)題错沃,在Spring-Mybatis源碼中不同的版本可能采用不同的方式實(shí)現(xiàn),筆者上文采用的是構(gòu)造器方式進(jìn)行屬性注入雀瓢,而Spring-Mybatis源碼也會(huì)采用set方式注入枢析,我們回到IDEA

然后我們直接進(jìn)入@MapperScan注解:

image

可以看到在Mybatis它使用到了我們?cè)诎咐袑懙降腀Import注解,我們跟蹤MapperScannerRegistrar這個(gè)類發(fā)現(xiàn)它實(shí)現(xiàn)了ImportBeanDefinitionRegistrar刃麸,我們找到registerBeanDefinitions方法

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
?
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
?
    if (resourceLoader != null) { // this check is needed in Spring 3.1
      scanner.setResourceLoader(resourceLoader);
    }
?
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }
?
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }
?
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }
?
    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
?
    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

前面都是一些準(zhǔn)備工作醒叁,我們直接看到最后一行 scanner.doScan(StringUtils.toStringArray(basePackages));點(diǎn)進(jìn)去:

@Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
?
    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
?
        if (logger.isDebugEnabled()) {
          logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
        }
?
        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        definition.setBeanClass(MapperFactoryBean.class);
?
        definition.getPropertyValues().add("addToConfig", this.addToConfig);
?
        boolean explicitFactoryUsed = false;
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
          definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionFactory != null) {
          definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
          explicitFactoryUsed = true;
        }
?
        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
          if (explicitFactoryUsed) {
            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
          }
          definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionTemplate != null) {
          if (explicitFactoryUsed) {
            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
          }
          definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
          explicitFactoryUsed = true;
        }
?
        if (!explicitFactoryUsed) {
          if (logger.isDebugEnabled()) {
            logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
          }
          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
      }
    }
?
    return beanDefinitions;
  }

簡(jiǎn)單解釋下他的代碼思路,獲取掃描出來(lái)的beanDefinitions泊业,然后對(duì)beanDefinition屬性設(shè)置把沼,注意這個(gè)beanDefinition,其實(shí)是一個(gè)MapperFactoryBean脱吱,在上面的代碼中有這么一句代碼definition.setBeanClass(MapperFactoryBean.class);意思就是將beanDefinition的class設(shè)置為MapperFactoryBean智政,所以Mybatis對(duì)將所有的創(chuàng)建的代理對(duì)象都轉(zhuǎn)換成MapperFactoryBean,而這個(gè)MapperFactoryBean是什么呢箱蝠?我們點(diǎn)開這個(gè)類

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>

它繼承了SqlSessionDaoSupport 還實(shí)現(xiàn)了FactoryBean,SqlSessionDaoSupport 就簡(jiǎn)單說(shuō)兩句垦垂,我們一直跟蹤SqlSessionDaoSupport宦搬,
image

會(huì)找到InitializingBean,InitializingBean是干什么的可能大家比較陌生劫拗,@PostConstruct大家都不陌生吧间校,其實(shí)他們兩個(gè)都差不多的,那Mybatis為什么實(shí)現(xiàn)了它呢页慷?其實(shí)目的就是在將Mapper交給Spring后自己還有其他的操作吧憔足,比如綁定xml的sql到方法上胁附,比如數(shù)據(jù)庫(kù)和對(duì)象之前的關(guān)系映射等等就不多說(shuō)了FactoryBean大家應(yīng)該有印象吧,我已在上文對(duì)它做了一些鋪墊滓彰,至于作用就不多說(shuō)了控妻,我們繼續(xù)回到代碼可以看到這有點(diǎn)區(qū)別 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());這里是并非采用的構(gòu)造器方式屬性注入,我們可以看到整個(gè)MapperFactoryBean都沒(méi)有構(gòu)造器去傳入我們的被代理接口揭绑,那他是怎么實(shí)現(xiàn)屬性注入的呢弓候?我看可以找到一個(gè)setXX的方法

image

說(shuō)明我們的Mybatis是采用的set方式去實(shí)現(xiàn)屬性注入的,有人可能有疑問(wèn)了他匪,為什么不用@Autowired的方式呢菇存?大家可以思考一下,我們的需要注入的屬性是不是class類型邦蜜,如果是class類型依鸥,我們對(duì)class注入有意義嗎?就好比每一個(gè)類都是Object類型悼沈,我們卻對(duì)Object注入類型一樣毕籽,毫無(wú)意義,而且Spring在屬性注入已經(jīng)排除了這種類型井辆,所以你就算想@Autowired也沒(méi)有辦法@Autowired关筒,好了說(shuō)了那么多,回到正軌杯缺,既然我們確認(rèn)需要setXXX的方式蒸播,那么我們?cè)趺赐ㄟ^(guò)代碼的方式改變它的注入類型呢?不知大家是否還記得AUTOWIRE_MODE萍肆,自動(dòng)裝配模式袍榆,記得我在IOC的文章提到過(guò)Spring對(duì)屬性注入的模式是NO,為什么時(shí)候塘揣,Spring認(rèn)為如果你對(duì)屬性沒(méi)有加@Autowired包雀,那么就不會(huì)對(duì)屬性進(jìn)行賦值,為什么這么做亲铡,其實(shí)也很簡(jiǎn)單才写,因?yàn)镾pring并不知道你的屬性需不需賦值,好了奖蔓,我看到AbstractBeanDefinition這個(gè)類

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
    implements BeanDefinition, Cloneable {
?
  /**
   * Constant that indicates no external autowiring at all.
   * @see #setAutowireMode
   */
  public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
?
  /**
   * Constant that indicates autowiring bean properties by name.
   * @see #setAutowireMode
   */
  public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
?
  /**
   * Constant that indicates autowiring bean properties by type.
   * @see #setAutowireMode
   */
  public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
?
  /**
   * Constant that indicates autowiring a constructor.
   * @see #setAutowireMode
   */
  public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
?
  
  @Deprecated
  public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
?
  private int autowireMode = AUTOWIRE_NO;

可以發(fā)現(xiàn)Spring確實(shí)默認(rèn)是 AUTOWIRE_NO;那么他還有其他幾種方式赞草,其中有AUTOWIRE_BY_TYPE,AUTOWIRE_BY_NAME 這么兩個(gè)模式吆鹤,這兩種模式都可以用來(lái)改變Spring注入的方式厨疙,改成SetXXX的方式,我們寫個(gè)實(shí)例:首先改造一下之前的MyImportBeanDefinitionRegistrar這個(gè)類

image

其次我們?cè)俑淖冎暗腗apperFactoryBean

image

然后debug一下將代碼停在setClazz上面

image

可以看到筆者所講的已經(jīng)得到驗(yàn)證疑务,是不是覺(jué)得Spring-Mybatis設(shè)計(jì)的十分優(yōu)雅啊沾凄,好了梗醇,對(duì)Spring-Mybatis的核心思想,筆者已經(jīng)對(duì)其描述完了撒蟀,相信真正理解到的讀者一定大有收獲叙谨,那么本章內(nèi)容就結(jié)束了,咱們下個(gè)世紀(jì)見牙肝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唉俗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子配椭,更是在濱河造成了極大的恐慌虫溜,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件股缸,死亡現(xiàn)場(chǎng)離奇詭異衡楞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)敦姻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瘾境,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人镰惦,你說(shuō)我怎么就攤上這事迷守。” “怎么了旺入?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵兑凿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我茵瘾,道長(zhǎng)礼华,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任拗秘,我火速辦了婚禮圣絮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雕旨。我一直安慰自己扮匠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布奸腺。 她就那樣靜靜地躺著餐禁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪突照。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天氧吐,我揣著相機(jī)與錄音讹蘑,去河邊找鬼末盔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛座慰,可吹牛的內(nèi)容都是我干的陨舱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼版仔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼游盲!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蛮粮,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤益缎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后然想,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莺奔,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年变泄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了令哟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妨蛹,死狀恐怖屏富,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛙卤,我是刑警寧澤狠半,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站表窘,受9級(jí)特大地震影響典予,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乐严,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一瘤袖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昂验,春花似錦捂敌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至甫恩,卻和暖如春逆济,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工奖慌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抛虫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓简僧,卻偏偏與公主長(zhǎng)得像建椰,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岛马,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355