[MyBatis源碼分析 - 反射器模塊 - 組件八] MetaObject

一钉鸯、簡介

??在對 ObjectWrapper 類的介紹中,除了 #hasGetter#hasSetter#getGetterType#getSetterType 這幾個方法舀射,其余帶 PropertyTokenizer 類型參數(shù)的方法中,參數(shù)代表的表達(dá)式已經(jīng)是經(jīng)過解析沒有子表達(dá)式的形式了如 order[0]怀伦,最終在 ObjectWrapper 中完成功能實(shí)現(xiàn)的最后一環(huán)脆烟,而表達(dá)式帶多層子表達(dá)式的初始狀態(tài)的解析過程正是在 MetaObject 類中進(jìn)行的,對 MetaObject 的分析房待,聚焦于這個屬性表達(dá)式的解析過程邢羔。

二、數(shù)據(jù)結(jié)構(gòu)

  private Object originalObject;                      // 原始JavaBean對象
  private ObjectWrapper objectWrapper;                // 封裝了originalObject對象的包裝對象
  private ObjectFactory objectFactory;                // 負(fù)責(zé)實(shí)例化originalObject對象的工廠對象
  private ObjectWrapperFactory objectWrapperFactory;  // 負(fù)責(zé)創(chuàng)建ObjectWrapper的工廠對象
  private ReflectorFactory reflectorFactory;          // 用于創(chuàng)建并緩存Reflcetor對象的工廠對象
  • originalObject:原始JavaBean對象桑孩。
  • objectWrapper:原始JavaBean對象對應(yīng)的包裝對象拜鹤,根據(jù)JavaBean的類型,可能為 BeanWrapper流椒、MapWrapper敏簿、CollectionWrapper
  • objectFactory:負(fù)責(zé)實(shí)例化originalObject對象的工廠對象,通常發(fā)生在使用表達(dá)式指定屬性值時惯裕,最末尾的屬性所在的對象為空的時候用到温数,這時候需要先實(shí)例化對象,如表達(dá)式 user.order.amount 蜻势,如果想給該表達(dá)式對應(yīng)的屬性賦值撑刺,如果中間的 order 對象是空的,則需要先實(shí)例化 order握玛,否則無法賦值猜煮。
  • objectWrapperFactory:負(fù)責(zé)創(chuàng)建ObjectWrapper的工廠對象,默認(rèn)實(shí)現(xiàn)拋出異常败许,主要方便用戶自定義 ObjectWrapperFactory 的實(shí)現(xiàn)。
  • reflectorFactory:用于創(chuàng)建并緩存Reflcetor對象的工廠對象淑蔚。

三市殷、構(gòu)造方法

  // 構(gòu)造方法聲明為私有,只允許通過 MetaObject.forObject() 靜態(tài)方法創(chuàng)建 MetaObject 對象
  private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    this.reflectorFactory = reflectorFactory;

    // 若原始對象已經(jīng)是ObjectWrapper對象刹衫,則直接使用
    if (object instanceof ObjectWrapper) {
      this.objectWrapper = (ObjectWrapper) object;
    // 若objectWrapperFactory能夠?yàn)樵撛紝ο髣?chuàng)建對應(yīng)的ObjectWrapper對象醋寝,則優(yōu)先使用
    // objectWrapperFactory,而DefaultObjectWrapperFactory.hasWrapperFor()始終返回false
    // 用戶可以自定義ObjectWrapperFactory實(shí)現(xiàn)進(jìn)行擴(kuò)展带迟。
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    // 若原始對象為Map類型音羞,則創(chuàng)建MapWrapper對象
    } else if (object instanceof Map) {
      this.objectWrapper = new MapWrapper(this, (Map) object);
    // 若原始對象是Collection類型,則創(chuàng)建CollectionWrapper對象
    } else if (object instanceof Collection) {
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    // 若原始對象是普通的JavaBean對象仓犬,則創(chuàng)建BeanWrapper對象
    } else {
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

【解析】
??如果傳入的對象是一個 ObjectWrapper 則直接使用嗅绰,否則嘗試用 ObjectWrapperFactory 創(chuàng)建 ObjectWrapper(假如用戶自定義實(shí)現(xiàn)可用),如果以上情況都不滿足搀继,則先后嘗試創(chuàng)建與對象類型相匹配的 ObjectWrapper窘面,可選類型為 MapWrapperCollectionWrapper叽躯、BeanWrapper财边。

四、方法功能

1点骑、MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory)

【功能】創(chuàng)建 MetaObject 對象的靜態(tài)方法酣难,也是唯一調(diào)用途徑,因?yàn)闃?gòu)造方法為 private黑滴。
【源碼與注解】

  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    if (object == null) {
      // 若Object為空憨募,則統(tǒng)一返回 SystemMetaObject.NULL_META_OBJECT
      // 反過來,若判斷到一個 JavaBean 對象對應(yīng)的 MetaObject 為 SystemMetaObject.NULL_META_OBJECT跷跪,可以判斷對象為空
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
  }

【解析】
??如果原始Java對象不為空馋嗜,則調(diào)用構(gòu)造方法創(chuàng)建對象,否則吵瞻,使用特殊的用來表達(dá)原始對象為空的 SystemMetaObject.NULL_META_OBJECT葛菇,SystemMetaObject 類的定義如下:

public final class SystemMetaObject {

  public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
  public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
  public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());

  private SystemMetaObject() {
    // Prevent Instantiation of Static Class
  }

  private static class NullObject {
  }

  public static MetaObject forObject(Object object) {
    return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
  }

}


2甘磨、Object getValue(String name)

【功能】獲取表達(dá)式指定的原始對象中屬性的值。
【源碼與注解】

  public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);   // 解析表達(dá)式
    if (prop.hasNext()) {
      // 創(chuàng)建子表達(dá)式所屬對象
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      // 如果所屬對象為空眯停,對象下的屬性肯定也為空
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        // 不為空济舆,則遞歸解析子表達(dá)式,這里是遞歸的入口
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      // 沒有子表達(dá)式或者到了遞歸出口莺债,直接調(diào)用 ObjectWrapper 對象獲得屬性值
      return objectWrapper.get(prop);
    }
  }

【解析】
??遞歸處理表達(dá)式的處理跟 ObjectWrapper 中的 #hasGetter滋觉、#hasSetter#getGetterType齐邦、#getSetterType 類似椎侠,MetaClass 類中有個方法 #metaClassForProperty() 用來為對象屬性創(chuàng)建對應(yīng)的 MetaClass 對象,同樣的 MetaObject 類中也有方法 #metaObjectForProperty() 用來為對象屬性創(chuàng)建對應(yīng)的 MetaObject 對象措拇,其源碼如下:

  public MetaObject metaObjectForProperty(String name) {
    Object value = getValue(name);
    return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
  }


3我纪、void setValue(String name, Object value)

【功能】為表達(dá)式指定的原始對象中屬性設(shè)置值。
【源碼與注解】

  public void setValue(String name, Object value) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      // 當(dāng)頂層屬性對象值為空時丐吓,依然嘗試去先創(chuàng)建初始化頂層屬性對象
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        // 如果要設(shè)定的值為空并且子表達(dá)不為空浅悉,這時沒有設(shè)置的必要了
        // 為什么非要并且子表達(dá)式為非空?因?yàn)橛锌赡芫褪且o某個屬性設(shè)空值
        if (value == null && prop.getChildren() != null) {
          // don't instantiate child path if value is null
          return;
        } else {
          // 初始化頂層屬性對象
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      // 如果值不為空券犁,直接調(diào)用术健;
      // 如果值為空,則先初始化后再調(diào)用
      metaValue.setValue(prop.getChildren(), value);
    } else {
      // 沒有子表達(dá)式或者到了遞歸出口粘衬,直接調(diào)用 ObjectWrapper 對象設(shè)置屬性值
      objectWrapper.set(prop, value);
    }
  }

【解析】
??#setValue()#getValue() 差別在于:如果頂層屬性為空荞估,肯定無法獲取值,直接返回null稚新;但是設(shè)置屬性值泼舱,即使頂層屬性為空,也可以先初始化頂層屬性枷莉,再賦值娇昙。比如 user.order.amount,屬性 user 是一個 User 對象笤妙,order 是其成員冒掌,類型為 Order,如果 order 為空蹲盘,獲取值時 order 的 amount 成員也必為空股毫,但是設(shè)置值時,即使 order 為空召衔,也可以先創(chuàng)建對象 order = new Order() 再給 order.amount 賦值铃诬。

4、其他方法

??除了上述方法,其他方法只是類中定義屬性的一個簡單的 getter趣席,或者是對 ObjectWrapper 接口方法的一個代理兵志,方便外部程序直接調(diào)用 MetaObject 類中的方法就可以實(shí)現(xiàn)相應(yīng)的功能。

  public ObjectFactory getObjectFactory() {
    return objectFactory;
  }

  public ObjectWrapperFactory getObjectWrapperFactory() {
    return objectWrapperFactory;
  }

  public ReflectorFactory getReflectorFactory() {
    return reflectorFactory;
  }

  public Object getOriginalObject() {
    return originalObject;
  }

  public String findProperty(String propName, boolean useCamelCaseMapping) {
    return objectWrapper.findProperty(propName, useCamelCaseMapping);
  }

  public String[] getGetterNames() {
    return objectWrapper.getGetterNames();
  }

  public String[] getSetterNames() {
    return objectWrapper.getSetterNames();
  }

  public Class<?> getSetterType(String name) {
    return objectWrapper.getSetterType(name);
  }

  public Class<?> getGetterType(String name) {
    return objectWrapper.getGetterType(name);
  }

  public boolean hasSetter(String name) {
    return objectWrapper.hasSetter(name);
  }

  public boolean hasGetter(String name) {
    return objectWrapper.hasGetter(name);
  }

  public ObjectWrapper getObjectWrapper() {
    return objectWrapper;
  }

  public boolean isCollection() {
    return objectWrapper.isCollection();
  }

  public void add(Object element) {
    objectWrapper.add(element);
  }

  public <E> void addAll(List<E> list) {
    objectWrapper.addAll(list);
  }


五宣肚、測試案例

1想罕、類定義

public class RichType {

  private RichType richType;

  private String richField;

  private String richProperty;

  private Map richMap = new HashMap();

  private List richList = new ArrayList() {
    {
      add("bar");
    }
  };

  public RichType getRichType() {
    return richType;
  }

  public void setRichType(RichType richType) {
    this.richType = richType;
  }

  public String getRichProperty() {
    return richProperty;
  }

  public void setRichProperty(String richProperty) {
    this.richProperty = richProperty;
  }

  public List getRichList() {
    return richList;
  }

  public void setRichList(List richList) {
    this.richList = richList;
  }

  public Map getRichMap() {
    return richMap;
  }

  public void setRichMap(Map richMap) {
    this.richMap = richMap;
  }
}


2、測試案例

@Test
  public void shouldGetAndSetField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richField", "foo");
    assertEquals("foo", meta.getValue("richField"));
  }

  @Test
  public void shouldGetAndSetNestedField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richField", "foo");
    assertEquals("foo", meta.getValue("richType.richField"));
  }

  @Test
  public void shouldGetAndSetProperty() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richProperty", "foo");
    assertEquals("foo", meta.getValue("richProperty"));
  }

  @Test
  public void shouldGetAndSetNestedProperty() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richProperty", "foo");
    assertEquals("foo", meta.getValue("richType.richProperty"));
  }

  @Test
  public void shouldGetAndSetMapPair() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richMap.key", "foo");
    assertEquals("foo", meta.getValue("richMap.key"));
  }

  @Test
  public void shouldGetAndSetMapPairUsingArraySyntax() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richMap[key]", "foo");
    assertEquals("foo", meta.getValue("richMap[key]"));
  }

  @Test
  public void shouldGetAndSetNestedMapPair() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richMap.key", "foo");
    assertEquals("foo", meta.getValue("richType.richMap.key"));
  }

  @Test
  public void shouldGetAndSetNestedMapPairUsingArraySyntax() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richMap[key]", "foo");
    assertEquals("foo", meta.getValue("richType.richMap[key]"));
  }

  @Test
  public void shouldGetAndSetListItem() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richList[0]", "foo");
    assertEquals("foo", meta.getValue("richList[0]"));
  }

  @Test
  public void shouldSetAndGetSelfListItem() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richList[0]", "foo");
    assertEquals("foo", meta.getValue("richList[0]"));
  }

  @Test
  public void shouldGetAndSetNestedListItem() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richList[0]", "foo");
    assertEquals("foo", meta.getValue("richType.richList[0]"));
  }

  @Test
  public void shouldGetReadablePropertyNames() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    String[] readables = meta.getGetterNames();
    assertEquals(5, readables.length);
    for (String readable : readables) {
      assertTrue(meta.hasGetter(readable));
      assertTrue(meta.hasGetter("richType." + readable));
    }
    assertTrue(meta.hasGetter("richType"));
  }

  @Test
  public void shouldGetWriteablePropertyNames() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    String[] writeables = meta.getSetterNames();
    assertEquals(5, writeables.length);
    for (String writeable : writeables) {
      assertTrue(meta.hasSetter(writeable));
      assertTrue(meta.hasSetter("richType." + writeable));
    }
    assertTrue(meta.hasSetter("richType"));
  }

  @Test
  public void shouldSetPropertyOfNullNestedProperty() {
    MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
    richWithNull.setValue("richType.richProperty", "foo");
    assertEquals("foo", richWithNull.getValue("richType.richProperty"));
  }

  @Test
  public void shouldSetPropertyOfNullNestedPropertyWithNull() {
    MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
    richWithNull.setValue("richType.richProperty", null);
    assertEquals(null, richWithNull.getValue("richType.richProperty"));
  }

  @Test
  public void shouldGetPropertyOfNullNestedProperty() {
    MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
    assertNull(richWithNull.getValue("richType.richProperty"));
  }

  @Test
  public void shouldVerifyHasReadablePropertiesReturnedByGetReadablePropertyNames() {
    MetaObject object = SystemMetaObject.forObject(new Author());
    for (String readable : object.getGetterNames()) {
      assertTrue(object.hasGetter(readable));
    }
  }

  @Test
  public void shouldVerifyHasWriteablePropertiesReturnedByGetWriteablePropertyNames() {
    MetaObject object = SystemMetaObject.forObject(new Author());
    for (String writeable : object.getSetterNames()) {
      assertTrue(object.hasSetter(writeable));
    }
  }

  @Test
  public void shouldSetAndGetProperties() {
    MetaObject object = SystemMetaObject.forObject(new Author());
    object.setValue("email", "test");
    assertEquals("test", object.getValue("email"));

  }

  @Test
  public void shouldVerifyPropertyTypes() {
    MetaObject object = SystemMetaObject.forObject(new Author());
    assertEquals(6, object.getSetterNames().length);
    assertEquals(int.class, object.getGetterType("id"));
    assertEquals(String.class, object.getGetterType("username"));
    assertEquals(String.class, object.getGetterType("password"));
    assertEquals(String.class, object.getGetterType("email"));
    assertEquals(String.class, object.getGetterType("bio"));
    assertEquals(Section.class, object.getGetterType("favouriteSection"));
  }

  @Test
  public void shouldDemonstrateDeeplyNestedMapProperties() {
    HashMap<String, String> map = new HashMap<String, String>();
    MetaObject metaMap = SystemMetaObject.forObject(map);

    assertTrue(metaMap.hasSetter("id"));
    assertTrue(metaMap.hasSetter("name.first"));
    assertTrue(metaMap.hasSetter("address.street"));

    assertFalse(metaMap.hasGetter("id"));
    assertFalse(metaMap.hasGetter("name.first"));
    assertFalse(metaMap.hasGetter("address.street"));

    metaMap.setValue("id", "100");
    metaMap.setValue("name.first", "Clinton");
    metaMap.setValue("name.last", "Begin");
    metaMap.setValue("address.street", "1 Some Street");
    metaMap.setValue("address.city", "This City");
    metaMap.setValue("address.province", "A Province");
    metaMap.setValue("address.postal_code", "1A3 4B6");

    assertTrue(metaMap.hasGetter("id"));
    assertTrue(metaMap.hasGetter("name.first"));
    assertTrue(metaMap.hasGetter("address.street"));

    assertEquals(3, metaMap.getGetterNames().length);
    assertEquals(3, metaMap.getSetterNames().length);

    Map<String,String> name = (Map<String,String>) metaMap.getValue("name");
    Map<String,String> address = (Map<String,String>) metaMap.getValue("address");

    assertEquals("Clinton", name.get("first"));
    assertEquals("1 Some Street", address.get("street"));
  }

  @Test
  public void shouldDemonstrateNullValueInMap() {
    HashMap<String, String> map = new HashMap<String, String>();
    MetaObject metaMap = SystemMetaObject.forObject(map);
    assertFalse(metaMap.hasGetter("phone.home"));

    metaMap.setValue("phone", null);
    assertTrue(metaMap.hasGetter("phone"));
    // hasGetter returns true if the parent exists and is null.
    assertTrue(metaMap.hasGetter("phone.home"));
    assertTrue(metaMap.hasGetter("phone.home.ext"));
    assertNull(metaMap.getValue("phone"));
    assertNull(metaMap.getValue("phone.home"));
    assertNull(metaMap.getValue("phone.home.ext"));

    metaMap.setValue("phone.office", "789");
    assertFalse(metaMap.hasGetter("phone.home"));
    assertFalse(metaMap.hasGetter("phone.home.ext"));
    assertEquals("789", metaMap.getValue("phone.office"));
    assertNotNull(metaMap.getValue("phone"));
    assertNull(metaMap.getValue("phone.home"));
  }

  @Test
  public void shouldNotUseObjectWrapperFactoryByDefault() {
    MetaObject meta = SystemMetaObject.forObject(new Author());
    assertTrue(!meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
  }

  @Test
  public void shouldUseObjectWrapperFactoryWhenSet() {
    MetaObject meta = MetaObject.forObject(new Author(), SystemMetaObject.DEFAULT_OBJECT_FACTORY, new CustomBeanWrapperFactory(), new DefaultReflectorFactory());
    assertTrue(meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));

    // Make sure the old default factory is in place and still works
    meta = SystemMetaObject.forObject(new Author());
    assertFalse(meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
  }

  @Test
  public void shouldMethodHasGetterReturnTrueWhenListElementSet() {
    List<Object> param1 = new ArrayList<Object>();
    param1.add("firstParam");
    param1.add(222);
    param1.add(new Date());

    Map<String, Object> parametersEmulation = new HashMap<String, Object>();
    parametersEmulation.put("param1", param1);
    parametersEmulation.put("filterParams", param1);

    MetaObject meta = SystemMetaObject.forObject(parametersEmulation);

    assertEquals(param1.get(0), meta.getValue("filterParams[0]"));
    assertEquals(param1.get(1), meta.getValue("filterParams[1]"));
    assertEquals(param1.get(2), meta.getValue("filterParams[2]"));

    assertTrue(meta.hasGetter("filterParams[0]"));
    assertTrue(meta.hasGetter("filterParams[1]"));
    assertTrue(meta.hasGetter("filterParams[2]"));
  }


3霉涨、執(zhí)行結(jié)果

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末按价,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子笙瑟,更是在濱河造成了極大的恐慌楼镐,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件往枷,死亡現(xiàn)場離奇詭異鸠蚪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)师溅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盾舌,“玉大人墓臭,你說我怎么就攤上這事⊙矗” “怎么了窿锉?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長膝舅。 經(jīng)常有香客問我嗡载,道長,這世上最難降的妖魔是什么仍稀? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任洼滚,我火速辦了婚禮,結(jié)果婚禮上技潘,老公的妹妹穿的比我還像新娘遥巴。我一直安慰自己,他們只是感情好享幽,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布铲掐。 她就那樣靜靜地躺著,像睡著了一般值桩。 火紅的嫁衣襯著肌膚如雪摆霉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音携栋,去河邊找鬼搭盾。 笑死,一個胖子當(dāng)著我的面吹牛刻两,可吹牛的內(nèi)容都是我干的增蹭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼磅摹,長吁一口氣:“原來是場噩夢啊……” “哼滋迈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起户誓,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤饼灿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帝美,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碍彭,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年悼潭,在試婚紗的時候發(fā)現(xiàn)自己被綠了庇忌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡舰褪,死狀恐怖皆疹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情占拍,我是刑警寧澤略就,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站晃酒,受9級特大地震影響表牢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贝次,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一崔兴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛔翅,春花似錦恼布、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盖腿,卻和暖如春爽待,著一層夾襖步出監(jiān)牢的瞬間损同,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工鸟款, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膏燃,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓何什,卻偏偏與公主長得像组哩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子处渣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359