[MyBatis源碼分析 - 反射器模塊 - 組件七] ObjectWrapper

一灸蟆、簡(jiǎn)介

??ObjectWrapper 接口是對(duì)對(duì)象的包裝隆敢,抽象了對(duì)象的屬性信息发皿,它定義了一系列查詢對(duì)象屬性信息、以及更新屬性的方法拂蝎,其類圖如下:



二穴墅、接口定義

public interface ObjectWrapper {
  // 根據(jù)表達(dá)式對(duì)應(yīng)的 PropertyTokenizer 對(duì)象,讀寫(xiě)對(duì)應(yīng)的屬性
  // 如果封裝的是集合類温自,則獲取指定/讀取key或下標(biāo)的value值
  Object get(PropertyTokenizer prop);
  void set(PropertyTokenizer prop, Object value);

  // 查找屬性表達(dá)式指定的屬性玄货,第二個(gè)參數(shù)表示是否忽略屬性表達(dá)式中的下劃線
  String findProperty(String name, boolean useCamelCaseMapping);

  // 獲取可讀/寫(xiě)屬性集合
  String[] getGetterNames();
  String[] getSetterNames();

  // 解析屬性表達(dá)式指定屬性的 setter/getter 方法的參數(shù)類型
  Class<?> getSetterType(String name);
  Class<?> getGetterType(String name);

  // 判斷屬性表達(dá)式指定屬性是否有 getter/setter 方法
  boolean hasSetter(String name);
  boolean hasGetter(String name);

  // 為屬性表達(dá)式指定的屬性創(chuàng)建響應(yīng)的 MetaObject 對(duì)象
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  
  boolean isCollection();           // 封裝的對(duì)象是否為 Collection 類型
  void add(Object element);         // 調(diào)用 Collection 中的 add() 方法
  <E> void addAll(List<E> element); // 調(diào)用 Collection 中的 addAll() 方法
}


三、BaseWrapper

??BaseWrapper 是實(shí)現(xiàn) ObjectWrapper 接口的抽象子類悼泌,主要為子類 BeanWrapperMapWrapper 提供屬性值的獲取和設(shè)置的功能松捉,包裝了 MetaObject 對(duì)象,關(guān)于對(duì) MetaObject 的介紹馆里,詳見(jiàn):

  protected static final Object[] NO_ARGUMENTS = new Object[0];
  protected MetaObject metaObject;

  protected BaseWrapper(MetaObject metaObject) {
    this.metaObject = metaObject;
  }

??BaseWrapper 對(duì)外暴露的方法解析如下隘世。

1、Object resolveCollection(PropertyTokenizer prop, Object object)

【功能】解析對(duì)象中的集合名鸠踪,調(diào)用 MetaObject 的方法獲取對(duì)應(yīng)的屬性值返回丙者。
【源碼與注解】

  protected Object resolveCollection(PropertyTokenizer prop, Object object) {
    // 如果表達(dá)式不合法解析不到屬性名,則直接返回默認(rèn)值
    if ("".equals(prop.getName())) {
      return object;
    } else {
      // 解析到屬性名营密,調(diào)用 MetaObject 的方法獲取屬性值返回
      return metaObject.getValue(prop.getName());
    }
  }


2蔓钟、Object getCollectionValue(PropertyTokenizer prop, Object collection)

【功能】根據(jù)屬性表達(dá)式,獲取對(duì)應(yīng)集合中的屬性值返回卵贱,這里的集合指 Map滥沫、List、Object[] 和基本類型數(shù)組键俱。
【源碼與注解】

  protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
    // 如果集合是一個(gè) Map 對(duì)象兰绣,則表達(dá)式中的索引就代表 Map 中對(duì)應(yīng)的 key,eg: map['key']
    if (collection instanceof Map) {
      return ((Map) collection).get(prop.getIndex());
    } else {
      // 如果集合是一個(gè)列表或數(shù)組编振,則下標(biāo)肯定是一個(gè)整數(shù)缀辩,eg: list[0]/arr[0]
      int i = Integer.parseInt(prop.getIndex());
      if (collection instanceof List) {
        return ((List) collection).get(i);
      } else if (collection instanceof Object[]) {
        return ((Object[]) collection)[i];
      } else if (collection instanceof char[]) {
        return ((char[]) collection)[i];
      } else if (collection instanceof boolean[]) {
        return ((boolean[]) collection)[i];
      } else if (collection instanceof byte[]) {
        return ((byte[]) collection)[i];
      } else if (collection instanceof double[]) {
        return ((double[]) collection)[i];
      } else if (collection instanceof float[]) {
        return ((float[]) collection)[i];
      } else if (collection instanceof int[]) {
        return ((int[]) collection)[i];
      } else if (collection instanceof long[]) {
        return ((long[]) collection)[i];
      } else if (collection instanceof short[]) {
        return ((short[]) collection)[i];
      } else {
        throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
      }
    }
  }


3、void setCollectionValue(PropertyTokenizer prop, Object collection, Object value)

【功能】設(shè)置表達(dá)式指定的集合中某個(gè)屬性的值踪央,跟上面的方法類似臀玄。
【源碼】

  protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
    if (collection instanceof Map) {
      ((Map) collection).put(prop.getIndex(), value);
    } else {
      int i = Integer.parseInt(prop.getIndex());
      if (collection instanceof List) {
        ((List) collection).set(i, value);
      } else if (collection instanceof Object[]) {
        ((Object[]) collection)[i] = value;
      } else if (collection instanceof char[]) {
        ((char[]) collection)[i] = (Character) value;
      } else if (collection instanceof boolean[]) {
        ((boolean[]) collection)[i] = (Boolean) value;
      } else if (collection instanceof byte[]) {
        ((byte[]) collection)[i] = (Byte) value;
      } else if (collection instanceof double[]) {
        ((double[]) collection)[i] = (Double) value;
      } else if (collection instanceof float[]) {
        ((float[]) collection)[i] = (Float) value;
      } else if (collection instanceof int[]) {
        ((int[]) collection)[i] = (Integer) value;
      } else if (collection instanceof long[]) {
        ((long[]) collection)[i] = (Long) value;
      } else if (collection instanceof short[]) {
        ((short[]) collection)[i] = (Short) value;
      } else {
        throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
      }
    }
  }


四、BeanWrapper

??BeanWrapper 繼承了抽象類 BaseWrapper畅蹂,因此也繼承了父類中的 MetaObject 對(duì)象健无,并且在構(gòu)造方法中根據(jù)傳入?yún)?shù)初始化該對(duì)象,BeanWrapper 還封裝了一個(gè) JavaBean 對(duì)象及對(duì)應(yīng)的 MetaClass 對(duì)象液斜,都通過(guò)傳入?yún)?shù)初始化和創(chuàng)建累贤,源碼如下:

  private Object object;        // JavaBean 對(duì)象
  private MetaClass metaClass;  // 保存 JavaBean 對(duì)象對(duì)應(yīng)的類的元信息的 MetaClass 對(duì)象

  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
  }

??BeanWrapper 實(shí)現(xiàn)了 BaseWrapper 接口中定義的所有方法叠穆,并自定義一些方法來(lái)輔助實(shí)現(xiàn),各個(gè)方法的分析如下:

1臼膏、Object get(PropertyTokenizer prop)

【功能】獲取JavaBean對(duì)象中對(duì)應(yīng)表達(dá)式的屬性的值硼被。

PS. 入?yún)?prop 代表的表達(dá)式是不帶子表達(dá)式的,即在調(diào)用 BeanWrapper 的上層代碼中渗磅,已經(jīng)完成了遞歸找到最末尾的子表達(dá)式的過(guò)程嚷硫。

【源碼與注解】

  @Override
  public Object get(PropertyTokenizer prop) {
    // (1)如果表達(dá)式帶索引,證明這是個(gè)集合(Map始鱼、List仔掸、數(shù)組)
    // 調(diào)用父類方法獲得對(duì)應(yīng)集合的對(duì)象,再根據(jù)索引獲取索引對(duì)應(yīng)的值
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      return getCollectionValue(prop, collection);
    } else {
      // (2)表達(dá)式不帶索引风响,調(diào)用getBeanProperty方法獲取屬性值
      return getBeanProperty(prop, object);
    }
  }

【解析】
(1)如果表達(dá)式形如:list[0]/map[key]/arr[0]嘉汰,則先獲取對(duì)應(yīng)屬性 list/map/arr 對(duì)象的值丹禀,再獲取索引對(duì)應(yīng)的元素的值状勤。
(2)如果表達(dá)式不帶索引,則傳入的就是個(gè)屬性名双泪,調(diào)用 #getBeanProperty() 方法持搜,獲取屬性對(duì)應(yīng)的值,其源碼分析如下:

  private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      // 得到獲取屬性對(duì)應(yīng)的Invoker對(duì)象
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        // 通過(guò)Invoker封裝的反射操作獲取屬性值
        return method.invoke(object, NO_ARGUMENTS);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable t) {
      throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
    }
  }


2焙矛、void set(PropertyTokenizer prop, Object value)

【功能】設(shè)置JavaBean對(duì)象中對(duì)應(yīng)表達(dá)式的屬性的值葫盼。
【源碼與注解】

  @Override
  public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      setCollectionValue(prop, collection, value);
    } else {
      setBeanProperty(prop, object, value);
    }
  }

【解析】
??跟 get 方法的實(shí)現(xiàn)類似,如果不帶索引村斟,調(diào)用 #setBeanProperty() 方法贫导,設(shè)置對(duì)應(yīng)屬性的值,其源碼如下:

  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      // 得到設(shè)置屬性對(duì)應(yīng)的Invoker對(duì)象
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = {value};
      try {
        // 通過(guò)Invoker封裝的反射操作設(shè)置屬性值
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }


3蟆盹、Class<?> getSetterType(String name)

【功能】獲得表達(dá)式對(duì)應(yīng)的屬性的 setter 方法的參數(shù)類型孩灯。
【源碼與注解】

  @Override
  public Class<?> getSetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);   // 解析表達(dá)式
    // 存在子表達(dá)式
    if (prop.hasNext()) {
      // 創(chuàng)建 MetaObject 對(duì)象
      MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
      // 如果 metaValue 為 SystemMetaObject.NULL_META_OBJECT,表示封裝的Java對(duì)象值為NULL
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        // 通過(guò)類元信息逾滥,獲取 setter 方法對(duì)應(yīng)屬性類型
        return metaClass.getSetterType(name);
      } else {
        // 當(dāng)對(duì)象不為空時(shí)峰档,通過(guò)對(duì)象元信息,獲取 setter 方法對(duì)應(yīng)屬性類型寨昙,可以獲得更具體的類型信息
        // 遞歸判斷子表達(dá)式的children讥巡,然后返回
        return metaValue.getSetterType(prop.getChildren());
      }
    } else {
      // 如果不存在子表達(dá)式,直接調(diào)用MetaClass.getSetter獲取屬性類型
      // 這里之所以用 metaClass.getSetterType(name) 而不是  metaValue.getSetterType(name)
      // 是因?yàn)?metaValue.getSetterType 也是依賴 objectWrapper.getSetterType舔哪,如果還是調(diào)用
      // metaValue.getSetterType 會(huì)陷入無(wú)限遞歸欢顷,metaClass 才是遞歸的出口
      return metaClass.getSetterType(name);
    }
  }

【解析】
??這里有個(gè)不太好理解的地方,為什么優(yōu)先使用 MetaObejct捉蚤,而僅當(dāng)其封裝的對(duì)象為空時(shí)吱涉,才使用 MetaClass 對(duì)象刹泄?MetaClass 封裝的是類的元信息,MetaObject 封裝的是對(duì)象的元信息怎爵,可以將類元信息看成是對(duì)象元信息的一個(gè)子集特石,所以根據(jù)類原信息得到的一些類型信息可能更加具體,舉個(gè)例子:

public class RichType {
    private RichType richType;
    private Map richMap = new HashMap();
    // other code
}

??RichType 中的屬性 richMap鳖链,在定義時(shí)姆蘸,并沒(méi)有指定具體的類型,所以 key 和 value 都可以是任意的 Object 的子類芙委,創(chuàng)建一個(gè)對(duì)象逞敷,并往 richMap 中壓值。

 RichType object = new RichType();
 object.setRichType(new RichType());
 object.getRichType().setRichMap(new HashMap());
 object.getRichType().getRichMap().put("haha", "123");
 object.getRichType().getRichMap().put("hehe", null);

??對(duì)于 object 來(lái)說(shuō)灌侣,上述代碼中壓入的兩個(gè)值對(duì)應(yīng)的表達(dá)式為 richType.richMap.haharichType.richMap.hehe推捐,對(duì)第一個(gè)表達(dá)式來(lái)說(shuō),其值為 "123"侧啼,所以其類型是 String牛柒,而第二個(gè)值或者其他沒(méi)有壓值的值,其值都是 null痊乾,其類型是 Object皮壁,測(cè)試代碼如下:

  @Test
  public void shouldGetSetterType() {
    RichType object = new RichType();
    object.setRichType(new RichType());
    object.getRichType().setRichMap(new HashMap());
    object.getRichType().getRichMap().put("haha", "123");
    //object.getRichType().getRichMap().put("hehe", null);
    MetaObject metaObject = SystemMetaObject.forObject(object);
    Class<?> hahaCls = metaObject.getObjectWrapper().getSetterType("richType.richMap.haha");
    Class<?> heheCls = metaObject.getObjectWrapper().getSetterType("richType.richMap.hehe");
    Assert.assertEquals(String.class, hahaCls);
    Assert.assertEquals(Object.class, heheCls);
  }

??執(zhí)行結(jié)果:



4、Class<?> getGetterType(String name)

【功能】獲得表達(dá)式對(duì)應(yīng)的屬性的 getter 方法的返回值類型哪审。
【源碼】
??跟 #getSetterType 的處理過(guò)程類似蛾魄。

  @Override
  public Class<?> getGetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return metaClass.getGetterType(name);
      } else {
        return metaValue.getGetterType(prop.getChildren());
      }
    } else {
      return metaClass.getGetterType(name);
    }
  }


5、boolean hasSetter(String name)

【功能】是否有表達(dá)式對(duì)應(yīng)的屬性的 setter 方法湿滓。
【源碼與注解】

  @Override
  public boolean hasSetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      // 如果找不到頂層屬性的 setter滴须,證明傳入的表達(dá)式對(duì)應(yīng)的屬性不存在
      if (metaClass.hasSetter(prop.getIndexedName())) {
        MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
        if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
          return metaClass.hasSetter(name);
        } else {
          return metaValue.hasSetter(prop.getChildren());
        }
      } else {
        return false;
      }
    } else {
      return metaClass.hasSetter(name);
    }
  }

【解析】
??處理流程跟 #getGetterType 還是大同小異,差別在于 #getGetterType 解析到不存在的屬性的時(shí)候會(huì)拋出異常叽奥,異常的源頭在于 Reflector.getGetterType()扔水,如下所示:

  public Class<?> getGetterType(String propertyName) {
    Class<?> clazz = getTypes.get(propertyName);
    if (clazz == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return clazz;
  }

??而本方法,會(huì)先判斷頂層是否有 setter而线,如果沒(méi)有子表達(dá)式的屬性肯定也是不存在的铭污,直接返回 false,實(shí)際上不做這一層判斷也沒(méi)事膀篮。

6嘹狞、boolean hasGetter(String name)

【功能】是否有表達(dá)式對(duì)應(yīng)的屬性的 getter 方法。
【源碼與注解】
??與 #hasSetter() 類似誓竿。

7磅网、MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory)

【功能】為表達(dá)式指定的屬性創(chuàng)建對(duì)應(yīng)的 MetaObject 對(duì)象。
【源碼與注解】

  @Override
  public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
    MetaObject metaValue;
    // 得到表達(dá)式指定屬性的類型
    Class<?> type = getSetterType(prop.getName());
    try {
      // 創(chuàng)建對(duì)應(yīng)的屬性對(duì)象
      Object newObject = objectFactory.create(type);
      // 創(chuàng)建屬性對(duì)應(yīng)的 MetaObject 對(duì)象
      metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
      // 為屬性所屬對(duì)象設(shè)置對(duì)應(yīng)的屬性值
      set(prop, newObject);
    } catch (Exception e) {
      throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
    }
    return metaValue;
  }


8筷屡、其他方法

??依賴 MetaClass 的方法實(shí)現(xiàn):

  • (1)#getGetterNames():調(diào)用 metaClass.getGetterNames() 實(shí)現(xiàn)涧偷。
  • (2)#getSetterNames():調(diào)用 metaClass.getSetterNames() 實(shí)現(xiàn)簸喂。
  • (3)#String findProperty(String, boolean):調(diào)用 metaClass.findProperty(String, boolean) 實(shí)現(xiàn)。

??不支持集合操作方法:

  @Override
  public boolean isCollection() {
    return false;
  }

  @Override
  public void add(Object element) {
    throw new UnsupportedOperationException();
  }

  @Override
  public <E> void addAll(List<E> list) {
    throw new UnsupportedOperationException();
  }


五燎潮、MapWrapper

??MapWrapper 同樣是 BaseWrapper 的子類喻鳄,在理解 BeanWrapper 的前提下,理解 MapWrapper 不在話下确封,對(duì)于 MapWrapper 的介紹除呵,聚焦跟 BeanWrapper 的差異即可,MapWrapper 封裝了一個(gè) Map 對(duì)象爪喘,代碼如下:

  private Map<String, Object> map;

  public MapWrapper(MetaObject metaObject, Map<String, Object> map) {
    super(metaObject);
    this.map = map;
  }

??獲取設(shè)置屬性值時(shí)颜曾,BeanWrapper 要先找到對(duì)應(yīng)的 Invoker 對(duì)象,再調(diào)用其 invoke() 方法秉剑,而 MapWrapper 直接調(diào)用 Map.get/set 即可泛豪,差異如下:


??獲取可讀可寫(xiě)屬性集合時(shí),MapWrapper 會(huì)獲取其鍵集合并轉(zhuǎn)化為數(shù)組侦鹏,如下:

??獲取屬性 getter诡曙、setter 遞歸處理出口時(shí),要獲取屬性對(duì)象种柑,再調(diào)用 #getClass() 獲取岗仑,如果值為空則默認(rèn)為 Obejct.class匹耕,源碼如下:

??判斷是否有表達(dá)式指定屬性的 getter/setter 的實(shí)現(xiàn)中聚请,setter 是固定返回 true,getter 則是根據(jù) key 判斷 Map 中是否有對(duì)應(yīng)的 value稳其,實(shí)現(xiàn)如下:

??其他方法的實(shí)現(xiàn)與 BeanWrapper 一致驶赏。

六、CollectionWrapper

??CollectionWrapper 封裝了一個(gè)集合對(duì)象既鞠,如下:

  private Collection<Object> object;

  public CollectionWrapper(MetaObject metaObject, Collection<Object> object) {
    this.object = object;
  }

??但對(duì)接口方法的實(shí)現(xiàn)實(shí)際上都是拋出一個(gè) UnsupportedOperationException 異常煤傍,表示不支持該操作。Collection 是一個(gè)接口嘱蛋,任何實(shí)現(xiàn)了該接口的類的實(shí)例都可以看成是一個(gè) Collection 對(duì)象蚯姆,實(shí)現(xiàn)接口的形式有多種多樣,在JDK中的默認(rèn)實(shí)現(xiàn)的類圖如下:



??不同的實(shí)現(xiàn)洒敏,會(huì)導(dǎo)致集合中元素的存儲(chǔ)形式和調(diào)用方法不一致龄恋,所以很難統(tǒng)一地實(shí)現(xiàn) ObjectWrapper 接口中定義的與集合無(wú)關(guān)的方法,其他三個(gè)集合相關(guān)方法依賴集合接口的方法凶伙,實(shí)現(xiàn)如下:

  @Override
  public boolean isCollection() {
    return true;
  }

  @Override
  public void add(Object element) {
    object.add(element);
  }

  @Override
  public <E> void addAll(List<E> element) {
    object.addAll(element);
  }


七郭毕、ObjectWrapperFactory

??ObjectWrapperFactory 是創(chuàng)建 ObjectWrapper 對(duì)象的工廠接口,默認(rèn)實(shí)現(xiàn)類為 DefaultObjectWrapper函荣,接口定義如下:

public interface ObjectWrapperFactory {
   // 是否有對(duì)象對(duì)應(yīng)的包裝類
  boolean hasWrapperFor(Object object);
   // 創(chuàng)建對(duì)象對(duì)應(yīng)的包裝類
  ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);
  
}

??DefaultObjectWrapperFactory 是默認(rèn)接口實(shí)現(xiàn)類显押,默認(rèn)為不支持創(chuàng)建對(duì)象對(duì)應(yīng)的包裝類扳肛,實(shí)際上就是不可用,源碼如下:

public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {

  @Override
  public boolean hasWrapperFor(Object object) {
    return false;
  }

  @Override
  public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
    throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
  }
}

??你可以會(huì)覺(jué)得有點(diǎn)疑惑乘碑,不可用為什么還會(huì)有這樣的一個(gè)默認(rèn)實(shí)現(xiàn)挖息,實(shí)際上這時(shí)設(shè)計(jì)中一種很常見(jiàn)的做法,可以將其看成是一個(gè)接口API的一個(gè)占位兽肤,使用接口的代碼面向接口方法編程旋讹,使用者可以按需插入自定義的實(shí)現(xiàn)類,而框架層代碼不會(huì)受到影響轿衔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沉迹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子害驹,更是在濱河造成了極大的恐慌鞭呕,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宛官,死亡現(xiàn)場(chǎng)離奇詭異葫松,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)底洗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)腋么,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人亥揖,你說(shuō)我怎么就攤上這事珊擂。” “怎么了费变?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵摧扇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挚歧,道長(zhǎng)扛稽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任滑负,我火速辦了婚禮在张,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘矮慕。我一直安慰自己帮匾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布凡傅。 她就那樣靜靜地躺著辟狈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哼转,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天明未,我揣著相機(jī)與錄音,去河邊找鬼壹蔓。 笑死趟妥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的佣蓉。 我是一名探鬼主播披摄,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼勇凭!你這毒婦竟也來(lái)了疚膊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤虾标,失蹤者是張志新(化名)和其女友劉穎寓盗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體璧函,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡傀蚌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蘸吓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片善炫。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖库继,靈堂內(nèi)的尸體忽然破棺而出箩艺,到底是詐尸還是另有隱情,我是刑警寧澤制跟,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布舅桩,位于F島的核電站酱虎,受9級(jí)特大地震影響雨膨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜读串,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一聊记、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恢暖,春花似錦排监、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至绅络,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谷暮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工盛垦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湿弦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓腾夯,卻偏偏與公主長(zhǎng)得像颊埃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝶俱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359