JavaWeb-三大器之监听器

参考文章:https://www.cnblogs.com/xdp-gacl/p/3969249.html

1. 概述

监听器,字面上的理解就是监听观察某个事件(程序)的发生情况,当被监听的事件真的发生了的时候,事件发生者(事件源) 就会给注册该事件的监听者(监听器)发送消息,告诉监听者某些信息,同时监听者也可以获得一份事件对象,根据这个对象可以获得相关属性和执行相关操作。
监听器模型涉及以下三个对象,模型图如下:

  • 事件
    用户对组件的一个操作,或者说程序执行某个方法,称之为一个事件
    如机器人程序执行工作。
  • 事件源
    发生事件的组件就是事件源,也就是被监听的对象
    如机器人可以工作,可以跳舞,那么就可以把机器人看做是一个事件源。
  • 事件监听器(处理器)
    监听并负责处理事件的方法
    如监听机器人工作情况,在机器人工作前后做出相应的动作,或者获取机器人的状态信息。

所以可以理解为:监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。
监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行。

2. 监听器执行过程

  • 给事件源注册监听器。
  • 组件接受外部作用,也就是事件被触发。
  • 组件产生一个相应的事件对象,并把此对象传递给与之关联的事件处理器。
  • 事件处理器启动,并执行相关的代码来处理该事件。

3. 监听器模式

事件源注册监听器之后,当事件源触发事件,监听器就可以回调事件对象的方法。
更形象地说,监听者模式是基于:注册-回调的事件/消息通知处理模式,就是被监控者将消息通知给所有监控者。

  • 注册监听器:事件源.setListener。
  • 回调:事件源实现onListener。

下面是一个模仿监听器的demo,需求:实现机器人工作和跳舞,在机器人开始工作和跳舞之前输出相关提示。

首先创建一个事件源Robot:

package com.tp.models.entity;

import com.tp.event.RobotEvent;
import com.tp.listners.RobotListener;

/**
 * FileName: Robot
 * Author:   TP
 * Description:事件源:机器人
 */
public class Robot {

    private RobotListener listener;

    /**
     * 注册机器人监听器
     *
     * @param listener 机器人监听器
     */
    public void registerListener(RobotListener listener) {
        this.listener = listener;
    }

    /**
     * 工作
     */
    public void working() {
        if (listener != null) {
            RobotEvent robotEvent = new RobotEvent(this);
            this.listener.working(robotEvent);
        }
        System.out.println("机器人开始工作......");
    }

    /**
     * 跳舞
     */
    public void dancing() {
        if (listener != null) {
            RobotEvent robotEvent = new RobotEvent(this);
            this.listener.dancing(robotEvent);
        }
        System.out.println("机器人开始跳舞......");
    }
}

创建事件对象RobotEvent:

package com.tp.event;

import com.tp.models.entity.Robot;

/**
 * FileName: RobotEvent
 * Author:   TP
 * Description:事件对象
 */
public class RobotEvent {

    private Robot robot;

    public RobotEvent() {
        super();
    }

    public RobotEvent(Robot robot) {
        super();
        this.robot = robot;
    }


    public Robot getRobot() {
        return robot;
    }

    public void setRobot(Robot robot) {
        this.robot = robot;
    }
}

创建事件监听器接口RobotListener:

package com.tp.listners;

import com.tp.event.RobotEvent;

/**
 * FileName: RobotListener
 * Author:   TP
 * Description:机器人事件监听器
 */
public interface RobotListener {

    /**
     * 监听工作
     * @param robotEvent 事件对象
     */
    void working(RobotEvent robotEvent);

    /**
     * 监听跳舞
     * @param robotEvent 事件对象
     */
    void dancing(RobotEvent robotEvent);
}

实现事件监听器RobotListener:

package com.tp.listners;

import com.tp.event.RobotEvent;
import com.tp.models.entity.Robot;

/**
 * FileName: RobotListenerImpl
 * Author:   TP
 * Description:
 */
public class RobotListenerImpl implements RobotListener {

    @Override
    public void working(RobotEvent robotEvent) {
        Robot robot = robotEvent.getRobot();
        System.out.println("机器人工作提示:您的机器人已经开始工作啦!");
    }

    @Override
    public void dancing(RobotEvent robotEvent) {
        Robot robot = robotEvent.getRobot();
        System.out.println("机器人跳舞提示:您的机器人已经开始跳舞啦!");
    }
}

编写测试类:

package com.tp.test.listener;

import com.tp.listners.RobotListenerImpl;
import com.tp.models.entity.Robot;
import org.junit.Test;

/**
 * FileName: RobotListenerTest
 * Author:   TP
 * Description:监听器测试
 */
public class RobotListenerTest {

    @Test
    public void robotListenerTest(){
        Robot robot = new Robot();
        robot.registerListener(new RobotListenerImpl());
        robot.working();
        robot.dancing();
    }
}

控制台输出效果:

4. JavaWeb中的监听器

4.1 基本概念

JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext,HttpSession和 ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

4.2 Servlet监听器的分类

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContext,HttpSession和ServletRequest这三个域对象
Servlet规范针对这三个对象上的操作,又把多种类型的监听器划分为三种类型:

  • 监听域对象自身的创建和销毁的事件监听器。
  • 监听域对象中的属性的增加和删除的事件监听器。
  • 监听绑定到HttpSession域中的某个对象的状态的事件监听器。

4.3 Servlet 8大监听器:

JavaWeb中有八大监听器
其中三大域对象各有一个生命周期监听器和属性操作监听器,共计6个;还有2个与HttpSession相关的感知型监听器。
事件源(监听对象):三大域

ServletContext域的监听器
  • 生命周期监听器:ServletContextListener
    ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。
    实现了ServletContextListener接口的类都可以对ServletContext对象的创建和销毁进行监听。
    当ServletContext对象被创建时,激发contextInitialized (ServletContextEvent sce)方法。
    当ServletContext对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。
    ServletContext域对象创建和销毁时机:
     创建:服务器启动针对每一个Web应用创建ServletContext
     销毁:服务器关闭前先关闭代表每一个web应用的ServletContext
  • 属性监听器:ServletContextAttributeListener
    用于监听域对象属性的增加,删除和替换的事件,它有三个方法:
    void attributeAdded(ServletContextAttributeEvent event):添加属性时调用;
    void attributeReplaced(ServletContextAttributeEvent event):替换属性时调用;
    void attributeRemoved(ServletContextAttributeEvent event):移除属性时调用;
HttpSession域的监听器
  • 生命周期监听器:HttpSessionListener
    HttpSessionListener 接口用于监听HttpSession对象的创建和销毁
    创建一个Session时,激发sessionCreated (HttpSessionEvent se)方法
    销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se) 方法。
  • 属性监听器:HttpSessioniAttributeListener
    用于监听域对象属性的增加,删除和替换的事件,它有三个方法:
    void attributeAdded(HttpSessionBindingEvent event):添加属性时调用
    void attributeReplaced(HttpSessionBindingEvent event):替换属性时调用
    void attributeRemoved(HttpSessionBindingEvent event):移除属性时调用
ServletRequest域的监听器
  • 生命周期监听器:ServletRequestListener
    ServletRequestListener接口用于监听ServletRequest 对象的创建和销毁
    Request对象被创建时,监听器的requestInitialized(ServletRequestEvent sre)方法将会被调用
    Request对象被销毁时,监听器的requestDestroyed(ServletRequestEvent sre)方法将会被调用
    ServletRequest域对象创建和销毁时机:
     创建:用户每一次访问都会创建request对象
     销毁:当前访问结束,request对象就会销毁
  • 属性监听器:ServletRequestAttributeListener
    用于监听域对象属性的增加,删除和替换的事件,它有三个方法:
    void attributeAdded(ServletRequestAttributeEvent srae):添加属性时调用
    void attributeReplaced(ServletRequestAttributeEvent srae):替换属性时调用
    void attributeRemoved(ServletRequestAttributeEvent srae):移除属性时调用
感知型监听器(都与HttpSession相关)

保存在Session域中的对象可以有多种状态:

  • 绑定(session.setAttribute("bean",Object))到Session中
  • 从 Session域中解除(session.removeAttribute("bean"))绑定
  • 随Session对象持久化到一个存储设备中
  • 随Session对象从一个存储设备中恢复

Servlet 规范中定义了两个特殊的监听器接口"HttpSessionBindingListener和HttpSessionActivationListener"来帮助JavaBean 对象了解自己在Session域中的这些状态:

  • HttpSessionBindingListener
    添加到javabean上,javabean就知道自己是否添加到session中了。
  • HttpSessionActivationListener
    Tomcat在钝化和活化session时,javabean感知到了后调用

它用来添加到JavaBean上,而不是添加到三大域上,注意⚠️:这两个监听器都不需要在web.xml中注册!

5. JavaWeb监听器使用示例

5.1 ServletContextListener监听器范例:

ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。
范例:编写一个MyServletContextListener类,实现ServletContextListener接口,监听ServletContext对象的创建和销毁:

package com.tp.listners;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * FileName: MyServletContextListener
 * Author:   TP
 * Description:MyServletContextListener类实现了ServletContextListener接口,
 *             因此可以对ServletContext对象的创建和销毁这两个动作进行监听。
 */
public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext对象创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext对象销毁");
    }
}

在web.xml文件中注册监听器
要想监听事件源,那么必须将监听器注册到事件源上才能够实现对事件源的行为动作进行监听,在JavaWeb中,监听的注册是在web.xml文件中进行配置的,如下:

<!-- 注册针对ServletContext对象进行监听的监听器 -->
    <listener>
        <description>ServletContextListener监听器</description>
        <!--实现了ServletContextListener接口的监听器类 -->
        <listener-class>com.tp.listeners.MyServletContextListener</listener-class>
    </listener>

启动项目,控制台输出:

5.2 HttpSessionListener监听器范例:

HttpSessionListener接口用于监听HttpSession对象的创建和销毁
范例:编写一个MyHttpSessionListener类,实现HttpSessionListener接口,监听HttpSession对象的创建和销毁:

package com.tp.listeners;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * FileName: MyHttpSessionListener
 * Author:   TP
 * Date:     2020-01-09 08:47
 * Description:MyHttpSessionListener类实现了HttpSessionListener接口,
 *             因此可以对HttpSession对象的创建和销毁这两个动作进行监听。
 */
public class MyHttpSessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession() + "创建了!!");
    }

   /*
   HttpSession的销毁时机需要在web.xml中进行配置,如下:
   <session-config>
       <session-timeout>1</session-timeout>
   </session-config>
   这样配置就表示session在1分钟之后就被销毁
   */
   
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("session销毁了!!");
    }
}

web.xml配置:

    <!--注册针对HttpSession对象进行监听的监听器-->
    <listener>
        <description>HttpSessionListener监听器</description>
        <listener-class>com.tp.listeners.MyHttpSessionListener</listener-class>
    </listener>
    <!-- 配置HttpSession对象的销毁时机 -->
    <session-config>
        <!--配置HttpSession对象的1分钟之后销毁 -->
        <session-timeout>1</session-timeout>
    </session-config>

当我们访问jsp页面时,HttpSession对象就会创建,此时就可以在HttpSessionListener观察到HttpSession对象的创建过程了,我们可以写一个jsp页面观察HttpSession对象创建的过程。
启动项目访问localhost:8080,观察控制台输出如下:

1分钟后Session自动销毁:

5.3 ServletRequestListener监听器范例:

ServletRequestListener接口用于监听ServletRequest 对象的创建和销毁
范例:编写一个MyServletRequestListener类,实现ServletRequestListener接口,监听ServletRequest对象的创建和销毁:

package com.tp.listeners;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

/**
 * FileName: MyServletRequestListener
 * Author:   TP
 * Description: MyServletRequestListener类实现了ServletRequestListener接口,
 *              因此可以对ServletRequest对象的创建和销毁这两个动作进行监听。
 */
public class MyServletRequestListener implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println(sre.getServletRequest() + "销毁了!!");

    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println(sre.getServletRequest() + "创建了!!");
    }
}

在web.xml文件中注册监听器:

<!--注册针对ServletRequest对象进行监听的监听器-->
<listener>
    <description>ServletRequestListener监听器</description>
    <listener-class>com.tp.listeners.MyServletRequestListener</listener-class>
</listener>

控制台效果:

5.4 ServletContextAttributeListener监听器范例:

编写ServletContextAttributeListener监听器监听ServletContext域对象的属性值变化情况,代码如下:

package com.tp.listeners;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import java.text.MessageFormat;

/**
 * FileName: MyServletContextAttributeListener
 * Author:   TP
 * Description: ServletContext域对象中属性的变更的事件监听器
 */
public class MyServletContextAttributeListener implements
        ServletContextAttributeListener {

    @Override
    public void attributeAdded(ServletContextAttributeEvent scab) {
        String str = MessageFormat.format(
                "ServletContext域对象中添加了属性:{0},属性值是:{1}"
                , scab.getName()
                , scab.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scab) {
        String str = MessageFormat.format(
                "ServletContext域对象中删除属性:{0},属性值是:{1}"
                , scab.getName()
                , scab.getValue());
        System.out.println(str);
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scab) {
        String str = MessageFormat.format(
                "ServletContext域对象中替换了属性:{0}的值"
                , scab.getName());
        System.out.println(str);
    }
}

web.xml中注册此监听器:

    <!--注册针对ServletContextAttribute进行监听的监听器-->
    <listener>
        <description>MyServletContextAttributeListener监听器</description>
        <listener-class>com.tp.listeners.MyServletContextAttributeListener</listener-class>
    </listener>

index.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
    <body>
        <h2>Hello World!</h2>
            <%
                //往application域对象中添加属性
                application.setAttribute("name", "TP");
                //替换application域对象中name属性的值
                application.setAttribute("name", "Java");
                //移除application域对象中name属性
                application.removeAttribute("name");
            %>
    </body>
</html>

启动项目,访问localhost:8080,观察控制台输出:

从运行结果中可以看到,ServletContextListener监听器成功监听到了ServletContext域对象(application)中的属性值的变化情况。
其他两个域对象属性监听器类似。

5.5 HttpSessionBindingListener监听器范例:

实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和 Session中删除的事件
当对象被绑定到HttpSession对象中时,web服务器调用该对象的void valueBound(HttpSessionBindingEvent event)方法
当对象从HttpSession对象中解除绑定时,web服务器调用该对象的void valueUnbound(HttpSessionBindingEvent event)方法
使用示例:

package com.tp.models.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

/**
 * FileName: Cat
 * Author:   TP
 * Description: 用于测试HttpSessionBindingListener的JavaBean
 */
@Data
@AllArgsConstructor
public class Cat implements HttpSessionBindingListener {

    private String name;

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println(String.format("%s被加入到session中了", name));
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println(String.format("%s从session中被移除了", name));
    }
}

index.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@page import="com.tp.models.entity.Cat"%>
<!DOCTYPE HTML>
<html>
    <body>
        <h2>Hello World!</h2>
            <%
                //往application域对象中添加属性
                application.setAttribute("name", "TP");
                //替换application域对象中name属性的值
                application.setAttribute("name", "Java");
                //移除application域对象中name属性
                application.removeAttribute("name");

                //将javabean对象绑定到Session中
                session.setAttribute("bean",new Cat("中国狸花猫"));
                //从Session中删除javabean对象
                session.removeAttribute("bean");
            %>
    </body>
</html>

启动项目,访问localhost:8080,观察控制台如下:

5.6 HttpSessionActivationListener监听器范例:

实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件

  • 当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中.
  • 当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该javabean对象的void sessionDidActive(HttpSessionEvent event)方法。这样javabean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存中

活化: javabean对象和Session一起被反序列化(活化)到内存中。
钝化: javabean对象存在Session中,当服务器把session序列化到硬盘上时,如果Session中的javabean对象实现了Serializable接口

如果Session中的javabean对象没有实现Serializable接口,那么服务器会先把Session中没有实现Serializable接口的javabean对象移除,然后再把Session序列化(钝化)到硬盘中

测试JavaBean:

package com.tp.models.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;

/**
 * FileName: Bird
 * Author:   TP
 * Description:测试HttpSessionActivationListener用的JavaBean
 */
@Data
@AllArgsConstructor
public class Bird implements HttpSessionActivationListener, Serializable {
    private static final long serialVersionUID = 7589841135210272124L;
    private String name;

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println(name + "和session一起被序列化(钝化)到硬盘了,session的id是:" + se.getSession().getId());
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println(name + "和session一起从硬盘反序列化(活化)回到内存了,session的id是:" + se.getSession().getId());
    }
}

为了观察绑定到HttpSession对象中的javabean对象随HttpSession对象一起被钝化到硬盘上和从硬盘上重新活化回到内存中的的过程,我们需要借助tomcat服务器帮助我们完成HttpSession对象的钝化和活化过程(配置tomcat服务器在关闭时将session持久化到本地)。
具体做法如下:
在webapp\META-INF文件夹下创建一个context.xml文件,如下所示:

context.xml中内容如下:

<Context>
    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
        <Store className="org.apache.catalina.session.FileStore" directory="/Users/tianpeng/Desktop/session"/>
    </Manager>
</Context>

在context.xml文件文件中配置了1分钟之后就将HttpSession对象钝化到本地硬盘的/Users/tianpeng/Desktop/session文件夹中

index.jsp改造如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@page import="com.tp.models.entity.Cat"%>
<%@page import="com.tp.models.entity.Bird"%>
<!DOCTYPE HTML>
<html>
    <body>
        <h3>一访问JSP页面,HttpSession就创建了,创建好的Session的Id是:${pageContext.session.id}</h3>
            <%
                //往application域对象中添加属性
                application.setAttribute("name", "TP");
                //替换application域对象中name属性的值
                application.setAttribute("name", "Java");
                //移除application域对象中name属性
                application.removeAttribute("name");

                //将javabean对象绑定到Session中
                session.setAttribute("bean",new Cat("中国狸花猫"));
                //从Session中删除javabean对象
                session.removeAttribute("bean");

                //javabean对象绑定到Session中
                session.setAttribute("bean",new Bird("百灵鸟"));
            %>
    </body>
</html>

启动项目,访问localhost:8080:
访问了index.jsp页面,服务器就会马上创建一个HttpSession对象,然后将实现了HttpSessionActivationListener接口的JavaBean对象绑定到session对象中,这个jsp页面在等待1分钟之后没有人再次访问,那么服务器就会自动将这个HttpSession对象钝化(序列化)到硬盘上:

控制台输出:


我们到指定的持久化session的目的文件夹中可以看到如下图所示:

我们将服务器关闭,此时session将失效,重新启动项目,再次访问localhost:8080,会发现服务器又会自动将已经钝化(序列化)到硬盘上HttpSession对象重新活化(反序列化)回到内存中。运行结果如下

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