# spring类型转换器(二)

spring类型转换器(二)

类型转换器Converter

Converter

除了使用PropertyEditor,spring自己还提供了另外一种类型转换器Converter,该接口声明了一个从源数据类型转换为目标数据类型的方法。只需要实现convert方法,该方法支持泛型

public interface Converter<S, T> {
   T convert(S source);
}

ConverterFactory接口,作为Converter工厂类,支持从一个原类型转换为一个目标类型对应的子类型。例如处理枚举类型、Number类型

public interface ConverterFactory<S, R> {
    //将S类型转换为T类型。
   <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

GenericConverter 用于支持多个类型对之间的转换。通过getConvertibleTypes声明所有支持转换的类型对。

public interface GenericConverter {
    //声明 所有支持转换的 类型对
    public Set<ConvertiblePair> getConvertibleTypes();
    //类型转换
    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

ConditionalGenericConverter 提供有条件的类型转换

public interface ConditionalConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

ConversionService

image

ConversionService 是策略模式的一种实现,主要用于提供一个入口,让外界选择注册的Converter.类型转换的功能还是由具体的Converter来实现。

public interface ConversionService {
    boolean canConvert(Class<?> sourceType, Class<?> targetType);
    <T> T convert(Object source, Class<T> targetType);
    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

ConverterRegistry接口 用于注册转换器

public interface ConverterRegistry {
    //注册converter
    void addConverter(Converter<?, ?> converter);
    //注册converter,避免对同一个converter重复注册
    <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);
    //注册GenericConverter
    void addConverter(GenericConverter converter);
    //ConverterFactory
    void addConverterFactory(ConverterFactory<?, ?> factory);
    //移除注册
    void removeConvertible(Class<?> sourceType, Class<?> targetType);
}

GenericConversionService实现了注册转换器、查找转换器、类型转换的功能,并将功能细节委托给了内部类Converters具体实现。Converters只接收GenericConverter类型的转换器。所以在GenericConversionService提供了ConverterAdapter和ConverterFactoryAdapter来完成Converter到GenericConverter、ConverterFactory到GenericConverter的转换,这里使用包装模式和适配模式的概念

//继承GenericConverter 并完成对Converter的包装
private final class ConverterAdapter implements ConditionalGenericConverter {
    private final Converter<Object, Object> converter;
    private final ConvertiblePair typeInfo;//封装源数据类型->目标数据类型
    private final ResolvableType targetType;

    public ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) {
        this.converter = (Converter<Object, Object>) converter;
        this.typeInfo = new ConvertiblePair(sourceType.resolve(Object.class), targetType.resolve(Object.class));
        this.targetType = targetType;
    }
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(this.typeInfo);
    }
    @Override //转换器的条件验证方法
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        // 判断目标类型是否一致.
        if (this.typeInfo.getTargetType() != targetType.getObjectType()) {
            return false;
        }
        // Full check for complex generic type match required?
        ResolvableType rt = targetType.getResolvableType();
        if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this.targetType) &&
            !this.targetType.hasUnresolvableGenerics()) {
            return false;
        }//如果被包装的Converter同时实现了ConditionalConverter,再次验证matches方法
        return !(this.converter instanceof ConditionalConverter) ||
            ((ConditionalConverter) this.converter).matches(sourceType, targetType);
    }
    @Override//调用被包装的converter进行类型转换
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) {//处理控制,主要针对Optional处理
            return convertNullSource(sourceType, targetType);
        }//调用包装的converter进行转换
        return this.converter.convert(source);
    }
}
private final class ConverterFactoryAdapter implements ConditionalGenericConverter {
    //被包装的 ConverterFactory 类
   private final ConverterFactory<Object, Object> converterFactory;
   private final ConvertiblePair typeInfo;//封装 源数据类型->目标数据类型 对

   public ConverterFactoryAdapter(ConverterFactory<?, ?> converterFactory, ConvertiblePair typeInfo) {
      this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
      this.typeInfo = typeInfo;
   }
   @Override//返回支持的转换的 源数据类型->目标数据类型 对
   public Set<ConvertiblePair> getConvertibleTypes() {
      return Collections.singleton(this.typeInfo);
   }
   @Override//保证验证所有实现了 ConditionConverter接口的matches方法
   public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
      boolean matches = true;
      if (this.converterFactory instanceof ConditionalConverter) {
         matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType);
      }
      if (matches) {
         Converter<?, ?> converter = this.converterFactory.getConverter(targetType.getType());//工厂类创建转换器
         if (converter instanceof ConditionalConverter) {//
            matches = ((ConditionalConverter) converter).matches(sourceType, targetType);
         }
      }
      return matches;
   }
    //类型转换
   public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType){
      if (source == null) {
         return convertNullSource(sourceType, targetType);
      }//调用包装的工厂方法创建转换器,并进行类型转换
      return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
   }
}

ConvertiblePair封装了源数据类型和目标数据类型对,覆写了hashCode方法,主要用于做map的key

final class ConvertiblePair {
   private final Class<?> sourceType;
   private final Class<?> targetType;
}
@Override
public int hashCode() {
    return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}

使用ConvertersForPair封装map的value,用于对应的转换器 list ,主要用于做map的value。注意这里的addFirst很重要,因为会有些默认注册的转换器,使用addFirst保证自定义的同种类型转换器放到默认的之前,可以保证自定义的转换器可以覆盖默认创建的

private static class ConvertersForPair {
    //类型可以对应多个转换器,所以这里用的是list
  private final LinkedList<GenericConverter> converters = new LinkedList<GenericConverter>();
    public void add(GenericConverter converter) {
        this.converters.addFirst(converter); //addFirst很重要
    }

  //获取converter的时候,再调用matches方法验证
 public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        for (GenericConverter converter : this.converters) {
            if (!(converter instanceof ConditionalGenericConverter) ||
                ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
                return converter;
            }
        }
        return null;
    }
}

注册

对于Optional的类型转换,无法直接声明带有泛型的类,所以需要在对象的字段类型上声明带泛型的Optional,实现类型转换

public static void main(String[] args) {
    DefaultConversionService conversionService = new DefaultConversionService();
    TestBean.ESex sex = conversionService.convert(0, ESex.class);
    System.out.println(sex);
    conversionService.addConverterFactory(new String2EnumConverter());
    EStatus status = conversionService.convert("3", EStatus.class);
    System.out.println(status);

    conversionService.addConverter(new LocalDateTime2StringConverter());
    LocalDateTime now = LocalDateTime.now();
    String nowStr = conversionService.convert(now, String.class);
    System.out.println(nowStr);
    }

GenericConversionService实现了ConverterRegistry接口,提供了注册转换器的具体实现。

//注册Converter
public void addConverter(Converter<?, ?> converter) {
    //获取类上的泛型信息
    ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
    if (typeInfo == null && converter instanceof DecoratingProxy) {
        typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
    }//注册converter
    addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
//注册ConverterFactory
public void addConverterFactory(ConverterFactory<?, ?> factory) {
    ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class); //获取类上的泛型信息
    //判断是否是代理类,如果是代理类 获取 被代理类上的泛型
    if (typeInfo == null && factory instanceof DecoratingProxy) {
        typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class);
    } //使用 ConverterFactoryAdapter 包装 ConverterFactory
    addConverter(new ConverterFactoryAdapter(factory,
                                             new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve())));
}
//调用Converters实现注册
public void addConverter(GenericConverter converter) {
    this.converters.add(converter);//调用内部类 Converters 的add方法实现注册转换器
    invalidateCache(); //清空缓存
}

在GenericConversionService中维护了一个内部类Converters对象。Converters中使用了一个Map<ConvertiblePair, ConvertersForPair>存储类型对和转换器之间的关系。注册类型转换器就是向这个map中put一条数据。GenericConverter .getConvertibleTypes方法申明的所有的类型对都会注册到map。

private final Set<GenericConverter> globalConverters = new LinkedHashSet<GenericConverter>();
//维护类型(ConvertiblePair)到转换器(ConvertersForPair)之间的关系
private final Map<ConvertiblePair, ConvertersForPair> converters =
      new LinkedHashMap<ConvertiblePair, ConvertersForPair>(36);

//添加功能
public void add(GenericConverter converter) {
    //获取converter所有支持的 源数据类型到目标数据类型对 
   Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
   if (convertibleTypes == null) {
      Assert.state(converter instanceof ConditionalConverter,
            "Only conditional converters may return null convertible types");
      this.globalConverters.add(converter);
   } else {//循环所有的类型对,添加到map中
      for (ConvertiblePair convertiblePair : convertibleTypes) {
          //建立map对应关系 添加到 converters中 完成注册
         ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
         convertersForPair.add(converter);//向列表首部添加
      }
   }
}
//先get null->new  nonull->add 感觉应该在这里  convertersForPair.add(converter);
private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
    ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
    if (convertersForPair == null) {
        convertersForPair = new ConvertersForPair();
        this.converters.put(convertiblePair, convertersForPair);
    }
    return convertersForPair;
}

查询

GenericConversionService 提供了根据源数据类型和目标数据类型查找类型转换器功能

public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
   return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
         TypeDescriptor.valueOf(targetType));
}

public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
   if (sourceType == null) {
      return true;
   }
   GenericConverter converter = getConverter(sourceType, targetType);
   return (converter != null);
}

在getConverter方法中调用 Converters查询map中维护的类型转换器,并将结果缓存起来

protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {  //先从缓存中查询
   ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
   GenericConverter converter = this.converterCache.get(key);
   if (converter != null) {
      return (converter != NO_MATCH ? converter : null);
   }
   //调用Converters查询
   converter = this.converters.find(sourceType, targetType);
   if (converter == null) { //返回 NoOpConverter
      converter = getDefaultConverter(sourceType, targetType);
   }//缓存
   if (converter != null) {
      this.converterCache.put(key, converter);
      return converter;
   }  //缓存空值,避免缓存穿透
   //private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
   this.converterCache.put(key, NO_MATCH);
   return null;
}
//NoOpConverter 不进行转换,直接返回
private static class NoOpConverter implements GenericConverter {
        @Override
        public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            return source;
        }
}

调用内部类Converters的find方法查询匹配的类型转换器,先根据源数据类型和目标数据类型查询,如果没有再循环父类和接口查询转换器

public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
   //获取源数据类型和目标数据类型的继承线上的类和接口
   List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
   List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
   for (Class<?> sourceCandidate : sourceCandidates) {
      for (Class<?> targetCandidate : targetCandidates) {
         ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
         GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
         if (converter != null) {
            return converter;
         }
      }
   }
   return null;
}
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
                              TypeDescriptor targetType, ConvertiblePair convertiblePair) {
    //从注册的map中 根据 convertiblePair查询转换器
    ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
    if (convertersForPair != null) { //验证实现了ConditionalConverter的matches方法
        GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
        if (converter != null) {
            return converter;
        }
    }
    // Check ConditionalConverters for a dynamic match
    for (GenericConverter globalConverter : this.globalConverters) {
        if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
            return globalConverter;
        }
    }
    return null;
}
//验证matches方法 找到第一个匹配的Converter,由于添加的时候是addFirst,自定义的添加的转换器一定在前面,所以这里能保证优先使用自定义添加的转换器,matches不过再查找默认注册的
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
    for (GenericConverter converter : this.converters) {
        if (!(converter instanceof ConditionalGenericConverter) ||
            ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
            return converter;
        }
    }
    return null;
}

类型转换

GenericConversionService实现了ConversionService接口,提供了类型转换的具体实现,集中为所有Converter、GenericConverter、ConverterFactory的类型转换功能提供了一个入口,在这里体现了策略模式,GenericConversionService相当于是策略模式中的Context,用于选择策略

public <T> T convert(Object source, Class<T> targetType) {
    return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
public Object convert(Object source, TypeDescriptor targetType) {
    return convert(source, TypeDescriptor.forObject(source), targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
    //获取converter
    GenericConverter converter = getConverter(sourceType, targetType);
    if (converter != null) {//调用Converter的convert方法类型转换
        Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
        return handleResult(sourceType, targetType, result);
    }
    return handleConverterNotFound(source, sourceType, targetType);
}
//调用converter的convert方法进行类型转换
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
    return converter.convert(source, sourceType, targetType);
}

ConversionServiceFactoryBean

在spring中使用ConversionServiceFactoryBean完成了集成ConversionService的功能,下面来看ConversionServiceFactoryBean的具体实现

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
   private Set<?> converters;
   private GenericConversionService conversionService;
   public void setConverters(Set<?> converters) {
      this.converters = converters;
   }
   @Override
   public void afterPropertiesSet() {
      this.conversionService = createConversionService();
      ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
   }
   protected GenericConversionService createConversionService() {
      return new DefaultConversionService();
   }
   @Override
   public ConversionService getObject() {
      return this.conversionService;
   }
    //....
}

从代码可以看出ConversionServiceFactoryBean实现了FactoryBean和InitializingBean,在初始化完成后创建了一个DefaultConversionService对象,然后调用ConversionServiceFactory的registerConverters注册自定义的类型转换器

public static void registerConverters(Set<?> converters, ConverterRegistry registry) {
   if (converters != null) {
      for (Object converter : converters) {
         if (converter instanceof GenericConverter) {
            registry.addConverter((GenericConverter) converter);
         } else if (converter instanceof Converter<?, ?>) {
            registry.addConverter((Converter<?, ?>) converter);
         } else if (converter instanceof ConverterFactory<?, ?>) {
            registry.addConverterFactory((ConverterFactory<?, ?>) converter);
         } else {
            throw new IllegalArgumentException("...");
         }
      }
   }
}

注册、查找和转换的功能都是在GenericConversionService中实现,那么DefaultConversionService继承自GenericConversionService,又做了什么扩展或者具体实现呢

public class DefaultConversionService extends GenericConversionService {
    
    private static volatile DefaultConversionService sharedInstance;
    public DefaultConversionService() {
        addDefaultConverters(this);
    }

    public static ConversionService getSharedInstance() {
        if (sharedInstance == null) {
            synchronized (DefaultConversionService.class) {
                if (sharedInstance == null) {
                    sharedInstance = new DefaultConversionService();
                }
            }
        }
        return sharedInstance;
    }
    public static void addDefaultConverters(ConverterRegistry converterRegistry) {
        addScalarConverters(converterRegistry);
        addCollectionConverters(converterRegistry);
        converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
        if (jsr310Available) {
            Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry);
        }
        converterRegistry.addConverter(new ObjectToObjectConverter());
        converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
        converterRegistry.addConverter(new FallbackObjectToStringConverter());
        if (javaUtilOptionalClassAvailable) {
            converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
        }
    }
}

看源码发现,DefaultConversionService提供了普通创建和单例创建两种模式,在ConversionServiceFactoryBean使用的是new,DefaultConversionService主要是在创建的时候注册了一系列的类型转换器

使用

实现LocalDateTime到String的转换

public class LocalDateTime2StringConverter implements Converter<LocalDateTime, String> {
    @Override
    public String convert(LocalDateTime source) {
        if(source == null){
            return "";
        }
        return source.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

针对枚举中有getCode方法的,使用 code值转换到枚举实例

public class String2EnumConverter implements ConverterFactory<String, Enum>, ConditionalConverter {

    @Override
    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new String2EnumConverter.String2Enum(getEnumType(targetType));
    }

    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        //限定该类型转换器 只针对有getCode方法的枚举
        try {
            Method getCodeMethod = targetType.getType().getMethod("getCode");
            if(getCodeMethod != null){
                return true;
            }
        } catch (NoSuchMethodException e) {
            return false;
        }
        return false;
    }

    private class String2Enum<T extends Enum> implements Converter<String, T> {
        private final Class<T> enumType;
        public String2Enum(Class<T> enumType) {
            this.enumType = enumType;
        }
        @Override
        public T convert(String source) {
            if (source.isEmpty()) {
                return null;
            }
            T[] enumConstants = enumType.getEnumConstants();
            try {//获取getCode方法
                Method getCode = enumType.getMethod("getCode");
                for(T obj : enumConstants){
                    //循环枚举中所有实例,并反射调用getCode方法获取code值,然后比较
                    if(source.trim().equals(getCode.invoke(obj))){
                        return obj;
                    }
                }
            } catch (Exception e) {
                return null;
            }
            return null;
        }
    }

    public Class<?> getEnumType(Class<?> targetType) {
        Class<?> enumType = targetType;
        while (enumType != null && !enumType.isEnum()) {
            enumType = enumType.getSuperclass();
        }
        if (enumType == null) {
            throw new RuntimeException("");
        }
        return enumType;
    }
}

Object转换到Optional,使用DefaultConversionService默认注册的。因为Optional是一个包装类型,所以转换是要转换为被Optional包装的内部类型。依赖conversionService根据Optional中的泛型类型转换。

@UsesJava8
final class ObjectToOptionalConverter implements ConditionalGenericConverter {

   private final ConversionService conversionService;

   public ObjectToOptionalConverter(ConversionService conversionService) {
      this.conversionService = conversionService;
   }
    
   @Override//声明支持Collection->Optional ,Object[] -> Optional, Object -> Optional的转换
   public Set<ConvertiblePair> getConvertibleTypes() {
      Set<ConvertiblePair> convertibleTypes = new LinkedHashSet<ConvertiblePair>(4);
      convertibleTypes.add(new ConvertiblePair(Collection.class, Optional.class));
      convertibleTypes.add(new ConvertiblePair(Object[].class, Optional.class));
      convertibleTypes.add(new ConvertiblePair(Object.class, Optional.class));
      return convertibleTypes;
   }

   @Override
   public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
      if (targetType.getResolvableType() != null) {
         return this.conversionService.canConvert(sourceType, new GenericTypeDescriptor(targetType));
      }
      else {
         return true;
      }
   }

   @Override
   public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
      if (source == null) {
         return Optional.empty();
      } else if (source instanceof Optional) {
         return source;
      } else if (targetType.getResolvableType() != null) {
         Object target = this.conversionService.convert(source, sourceType, new GenericTypeDescriptor(targetType)); //转换为泛型
         if (target == null || (target.getClass().isArray() && Array.getLength(target) == 0) ||
                  (target instanceof Collection && ((Collection) target).isEmpty())) {
            return Optional.empty();
         }
         return Optional.of(target);
      } else {
         return Optional.of(source);
      }
   }
   @SuppressWarnings("serial")
   private static class GenericTypeDescriptor extends TypeDescriptor {
        //使用Optional中的泛型进行转换
      public GenericTypeDescriptor(TypeDescriptor typeDescriptor) {
         super(typeDescriptor.getResolvableType().getGeneric(), null, typeDescriptor.getAnnotations());
      }
   }

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342