Spring5MVC——request与Controller方法映射的创建

SpringMVC的核心流程

  • 建立请求和Controller方法的映射集合的流程。

  • 根据请求查找对应的Controller方法的流程。

  • 请求参数绑定到方法形参,执行方法处理请求,返回结果进行视图渲染的流程。

HandlerMapping

HandlerMapping接口作用是将请求映射到处理程序,以及预处理和处理后的拦截器列表,映射是基于一些标准的,其中的细节因不同的实现而不相同。这是官方文档上一段描述,该接口只有一个方法getHandler(request),返回一个HandlerExecutionChain对象,接口本身很简单。

public interface HandlerMapping {


    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";


    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";


    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";


    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";


    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";


    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";


    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";


    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    // 返回请求的一个处理程序handler和拦截器interceptors
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

DispatcherServlet

DispatcherServlet是Spring MVC核心,它是J2EE规范前端控制器的实现,负责拦截用户请求,并解析请求进行转发。

public class DispatcherServlet extends FrameworkServlet {

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        //初始化文件上传处理器
        initMultipartResolver(context);
        
        //初始化国际化配置
        initLocaleResolver(context);

        //初始化主题处理器
        initThemeResolver(context);

        //初始化HanlderMapping
        initHandlerMappings(context);

        //初始化HandlerAdapter
        //HandlerAdapter用来调用具体的方法对用户发来的请求来进行处理
        initHandlerAdapters(context);

        //初始化异常处理器,
        // HandlerExceptionResolver是用来对请求处理过程中产生的异常进行处理
        initHandlerExceptionResolvers(context);

        //RequestToViewNameTranslator用于在视图路径为空的时候,自动解析请求
        //去获取ViewName
        initRequestToViewNameTranslator(context);

        //初始化视图处理器
        //ViewResolvers将逻辑视图转成view对象
        initViewResolvers(context);

        //FlashMapManager用于存储、获取以及管理FlashMap实例
        initFlashMapManager(context);
    }
}

从方法调用链看,DispatcherServlet的initStrategies方法是在OnRefresh方法之后调用的,而initHandlerMappings方法是在initStrategies方法中被调用的。

initHandlerMappings

  • 初始化HanlderMapping
public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        //是否检查所有的HandlersMapping实现类并载入,默认为true
        //<init-param>
        //      <param-name>detectAllHandlerMappings</param-name>
        //      <param-value>false</param-value>
        //</init-param>
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            //寻找IOC容器中HandlerMapping类型的Bean
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                // 对找到的HandlerMapping类型的Bean列表进行排序
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                //从容器里获取beanName为handlerMapping的Bean
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        // 从context上下文中定义HandlerMapping时,Spring MVC将使用默认HandlerMapping,默认的HandlerMapping在DispatcherServlet.properties属性文件中定义,
        // 该文件是在DispatcherServlet的static静态代码块中加载的
        // 默认的是:BeanNameUrlHandlerMapping和RequestMappingHandlerMapping
        if (this.handlerMappings == null) {
            //如果以上过程都没有找到handlerMapping
            //将赋值handlerMappings为默认的HandlerMapping
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }
    }
}

这就是HanlderMapping的初始化过程。

HandlerMapping的实现类

从方法调用链可以得知,SpringMvc有四种HandlerMapping:

  • requestMappingHandlerMapping -> RequestMappingHandlerMapping
  • beanNameHandlerMapping -> BeanNameUrlHandlerMapping
  • routerFunctionMapping -> RouterFunctionMapping
  • defaultServletHandlerMapping -> SimpleUrlHandlerMapping

这里主要关注RequestMappingHandlerMapping

RequestMappingHandlerMapping继承类图
  • 由于RequestMappingHandlerMapping实现了ApplicationContextAware和ServletContextAware两个接口,这说明RequestMappingHandlerMapping可以通过这两个接口获取到Root容器和Servley子容器中的Bean。

  • RequestMappingHandlerMapping还实现了InitializingBean接口,该接口的afterPropertiesSet方法是在bean初始化的invokeInitMethods方法之后执行的,因此可以在这里加入对标记有RequestMapping标记的Bean进行处理,将相关的映射关系依次保存到RequestMappingHandlerMapping或者其父类的成员变量里面。

AbstractHandlerMethodMapping

在类图中AbstractHandlerMethodMapping实现了InitializingBean接口,重写了afterPropertiesSet方法

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }
}

从方法调用链可以看出:

  • 程序在Bean初始化后,会立即执行AbstractHandlerMethodMapping中的afterPropertiesSet方法,这是因为RequestMappingHandlerMapping是需要先在容器中创建出来,后续才能在DispatcherServlet中的initHandlerMappings方法中去作为HandlerMapping的实现类给载入到DispatcherServlet的成员变量handlerMappings集合里。

  • 又因为RequestMappingHandlerMapping在创建的时候会去调用invokeInitMethods方法进行初始化,也会执行afterPropertiesSet里面的逻辑,也就是会先执行initHandlerMethods的逻辑去初始化映射关系。

AbstractHandlerMethodMapping#initHandlerMethods

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void initHandlerMethods() {
        //遍历容器里所有的Bean
        for (String beanName : getCandidateBeanNames()) {
            //忽略掉scopedTarget.打头的bean(session application request之类的作用域内的代理类)
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected String[] getCandidateBeanNames() {
        //从root容器以及子容器里,或者仅从子容器里获取所有的Bean
        return (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
                obtainApplicationContext().getBeanNamesForType(Object.class));
    }
}
  • 根据detectHandlerMethodsInAncestorContexts变量是否为true,来决定是否需要从root容器以及子容器里,或者仅从子容器里获取所有的Bean。
  • 如果为false,则只从当前的子容器即ServletContext里去查找。
  • 而如果Root容器中有bean被标记上RequestMapping的话,detectHandlerMethodsInAncestorContexts就会被标记为true。

AbstractHandlerMethodMapping#processCandidateBean

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            //获取Bean的Class类型
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isTraceEnabled()) {
                logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
            }
        }
        //判断Class上是否有Controller注解或是RequestMapping注解
        if (beanType != null && isHandler(beanType)) {
            //提取其url与controller映射关系
            detectHandlerMethods(beanName);
        }
    }
}


public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    @Override
    protected boolean isHandler(Class<?> beanType) {
        //判断类上是否存在Controller注解或是RequestMapping注解
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
}
  • 首先获取Bean对应的Class对象。
  • 通过判断Class上是否有Controller注解或是RequestMapping注解,为后续提取其url与controller映射关系做好准备。

AbstractHandlerMethodMapping#detectHandlerMethods

  • 发觉Controller方法,并建立与请求url的映射关系。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void detectHandlerMethods(Object handler) {
        //如果handler是字符串,证明是一个beanName,则从IOC容器中获取其Class对象;否则直接获取Class对象
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            //为了确保获取到的类是被代理的类
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            //寻找方法上有@RequestMapping注解的Method实例
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            if (logger.isTraceEnabled()) {
                logger.trace(formatMappings(userType, methods));
            }
            //将获取到的Method对象依次注册到HandlerMapping中去
            methods.forEach((method, mapping) -> {
                //获取被AOP代理包装后的方法实例
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

RequestMappingHandlerMapping#getMappingForMethod

  • 创建求映射信息对象RequestMappingInfo。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        //创建方法上面的RequestMapping信息
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            //创建类上面的RequestMapping信息
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                //将两个信息合并
                info = typeInfo.combine(info);
            }
            String prefix = getPathPrefix(handlerType);
            if (prefix != null) {
                info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
            }
        }
        return info;
    }

    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        //如果该函数含有@RequestMapping注解,则根据其注解信息生成RequestMapping实例,
        //否则返回空
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
        //这里用到了一个典型的建造者模式
        RequestMappingInfo.Builder builder = RequestMappingInfo
                //这里对路径进行解析,在path中是支持SpEL表达式的,
                //RequestMappingHandlerMapping实现了EmbeddedValueResolverAware这个接口
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name());
        if (customCondition != null) {
            builder.customCondition(customCondition);
        }
        return builder.options(this.config).build();
    }
}

视线拉回到detectHandlerMethods方法中:

  • 在执行完AbstractHandlerMethodMapping类中的detectHandlerMethods方法中的selectMethods方法之后,就能建立起Controller方法实例和RequestMappingInfo的映射关系,并将相关的映射保存到methods这个Map<Method, T>集合中,key为方法实例,value为RequestMappingInfo实例。

  • 之后会遍历methods将相关的映射信息给注册到HandlerMapping中。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void detectHandlerMethods(Object handler) {
        ......

        if (handlerType != null) {
            ......

            //将获取到的Method对象依次注册到HandlerMapping中去
            methods.forEach((method, mapping) -> {
                //获取被AOP代理包装后的方法实例
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }
}

上面这段代码,会将methods中的Method实例和RequestMappingInfo实例给一一对应的注册到HandlerMapping里面。

AbstractHandlerMethodMapping#registerHandlerMethod

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }

    class MappingRegistry {
        //储存 MappingRegistration 所有的注册信息
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        //储存RequestMappingInfo 与 HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        //储存路径与RequestMappingInfo
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        //储存@RequestMapping 注解的请求路径 与 HandlerMethod列表
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
        //跨域配置
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        //读写锁
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        /**
         * 注册数据:  mapping => RequestMappingInfo ||  handler => beanName  ||  method => Method
         *      1、根据 handle 和 method,创建 HandlerMethod,
         *      2、效验 HandlerMethod 是否存在
         *      3、储存 HandlerMethod
         *      4、储存 RequestMappingInfo 跟 url
         *      5、储存 @RequestMapping 注解 的路径跟所有的方法
         *      6、存储 CorsConfiguration 信息(跨域)
         *      7、储存 MappingRegistration 对象
         */
        public void register(T mapping, Object handler, Method method) {
            // Assert that the handler method is not a suspending one.
            if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
                    throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
                }
            }
            this.readWriteLock.writeLock().lock();
            try {
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                //验证方法的唯一性,即先前是否已经注册过同样的映射
                validateMethodMapping(handlerMethod, mapping);
                //注册RequestMappingInfo 和 HandlerMethod
                this.mappingLookup.put(mapping, handlerMethod);
                //注册请求路径与对应的RequestMappingInfo
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null) {
                    //注册请求路径与HandlerMethod
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    //注册HandlerMethod与跨域信息
                    this.corsLookup.put(handlerMethod, corsConfig);
                }
                //创建及注册 MappingRegistation 信息
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }
    }
}

主要步骤:

  • 1、根据 handle 和 method,创建 HandlerMethod,
  • 2、效验 HandlerMethod 是否存在,验证方法的唯一性,即先前是否已经注册过同样的映射
  • 3、注册RequestMappingInfo 和 HandlerMethod
  • 4、注册请求路径与对应的RequestMappingInfo
  • 5、注册请求路径与HandlerMethod
  • 6、注册HandlerMethod与跨域信息
  • 7、创建及注册 MappingRegistation 信息

最终会将前面获取到的所有信息给包装起来,保存到Map<T, MappingRegistration<T>> registry成员变量中,后续就可以解析请求并选择合适的Controller方法来对请求进行处理。

这样就完成了建立请求和Controller方法的映射集合的流程的分析。

HandlerAdapter

public interface HandlerAdapter {


    //判断适配器是否适配handler,适配策略由子类实现
    boolean supports(Object handler);


    //使用适配的handler执行用户请求
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;


    //返回资源的最后修改时间,如果handler实现类不支持可以返回-1
    long getLastModified(HttpServletRequest request, Object handler);

}

以上是HandlerAdapter接口的源码分析,如需自定义HandlerAdapter,只需要实现该接口,在supports方法中定义适配策略,并实现handle方法进行调用即可。

HandlerAdapter的初始化

顾名思义,是handler的适配器,它能处理参数转换为handler能接受的数据类型,解析参数、处理返回值等。

在DispatcherServlet进行初始化流程调用initStrategies,执行完initHandlerMappings方法后,会接着执行initHandlerAdapters方法。

initHandlerAdapters

  • 初始化HandlerAdapter
public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerAdapters(ApplicationContext context) {
        this.handlerAdapters = null;
        // 在部署描述文件中可控制该参数
        if (this.detectAllHandlerAdapters) {
            // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
            // 从应用上下文中查找HandlerAdapter
            Map<String, HandlerAdapter> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList<>(matchingBeans.values());
                // We keep HandlerAdapters in sorted order.
                // 对使用的HandlerAdapter进行排序,spring提供的只有RequestMappingHandlerAdapter实现了Ordered接口,其他都不具备排序功能
                AnnotationAwareOrderComparator.sort(this.handlerAdapters);
            }
        }
        else {
            try {
                // 如果在部署描述文件中配置了detectAllHandlerAdapters=false,
                // 此时spring会加载名称为handlerAdapter的bean为处理器适配器
                HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                // 转化为集合赋给handlerAdapters属性
                this.handlerAdapters = Collections.singletonList(ha);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerAdapter later.
            }
        }

        // Ensure we have at least some HandlerAdapters, by registering
        // default HandlerAdapters if no other adapters are found.
        // 如果未配置HandlerAdapter,注册默认的处理器适配器,
        // 即从DispatcherServlet.properties中获取的HttpRequestHandlerAdapter、
        // SimpleControllerHandlerAdapter和ReqeustMappingHandlerAdapter
        if (this.handlerAdapters == null) {
            this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }
    }
}

通过initHandlerAdapters方法的调用栈可知,一共向List<HandlerAdapter> handlerAdapters集合中注册了4个HandlerAdapter的实现类。

  • requestMappingHandlerAdapter -> RequestMappingHandlerAdapter

  • handlerFunctionAdapter -> HandlerFunctionAdapter

  • httpRequestHandlerAdapter -> HttpRequestHandlerAdapter

  • simpleControllerHandlerAdapter -> SimpleControllerHandlerAdapter

以上就是Spring MVC对HandlerAdapter组件的注册过程。

参考:
https://segmentfault.com/a/1190000014901736

https://segmentfault.com/a/1190000015009343

https://segmentfault.com/a/1190000015027885

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