第 1 章 Spring 的基本应用

Spring 是当前主流的 Java Web 开发框架,它是为了解决企业应用开发的复杂性问题而产生 的。 对于一个 Java 开发者来说,掌握 Spring 框架的使用,已是其必备的技能之一。 本章将对 Spring 框架的基础知识进行详细的讲解。

Spring 概述
  • 什么是 Spring
  • Spring 是由 Rod Johnson 组织和开发的一个分层的 Java SE/EE fu川一stack (一站式)轻量 级开源框架,它以 loC ( Inversion of Control ,控制反转)和 AOP ( Aspect Oriented Programming , 面向切面编程)为内核,使用基本的 JavaBean 来完成以前只可能由 EJB ( Enterprise Java Beans , Java 企业 Bean) 完成的工作,取代了 EJB 的靡肿、 低效的开发模式。
  • Spring 致力于 Java EE 应用各层的解决方案,在表现层它提供了 Spring MVC 以及与 Struts 框架的整合功能;在业务逻辑层可以管理事务、 记录日志等;在持久层可以整合 MyBatis、 Hibernate、 JdbcTemplate 等技术。 因此,可以说 Spring 是企业应用开发很好的"一站式"选 择。 虽然 Spring 贯穿于表现层、 业务逻辑层和持久层,但它并不想取代那些已有的框架,而是 以高度的开放性与它们进行无缝整合。
  • Spring 框架的优点

Spring 具有简单、可测试和松辑合等特点,从这个角度出发, Spring 不仅可以用于服务器 端开发,也可以应用于任何 Java 应用的开发中。 关于 Spring 框架优点的总结,具体如下。

  • 非侵入式设计
    Spring 是一种非侵入式( non-invasive )框架,它可以使应用程序代码对框架的依赖 、最小化。
  • 方便解耦、 简化开发
    Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给 Spring 容器 管理,大大地降低了组件之间的搞合性。
  • 支持 AOP
    Spring 提供了对 AOP 的支持,它允许将一些通用任务,如安全、 事务、 曰志等进行集中式 处理,从而提高了程序的复用性。
  • 支持声明式事务处理
    只需要通过配置就可以完成对事务的管理,而无须手动编程。
  • 方便程序的测试
    Spring 提供了对 Junit4 的支持,可以通过注解方便地测试 Spring 程序。
  • 方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts、 Hibernate、 MyBatis、 Quartz 等)的直接支持。
  • 降低 Java EE API 的使用难度
    Spring 对 Java EE 开发中非常难用的一些 API (如 JDBC、 JavaMail 等),都提供了封装, 使这些 API 应用难度大大降低。
  • Spring 的体系结构

Spring 框架采用的是分层架构,它一系列的功能要素被分成 20 个模块,这些模块大体分为 Core Container、 Data Access/lntegration、 Web、 AOP (Aspect Oriented Programming)、 Instrwmentation, Messaging 和 Test ,如图所示。



在图中,包含了 Spring 框架的所有模块,其中,灰色背景模块为本书中所涉及的主要 模块。 接下来分别对体系结构中的模块作用进行简单介绍,具体如下。

  1. Core Container (核心窑器)
    Spring 的核心容器是其他模块建立的基础,它主要由 Beans 模块、 Core 模块、 Context 模 块、 Context-support 模块和 SpEL ( Spring Expression Language , Spring 表达式语言)模块 组成,具体介绍如下。
    • Beans 模块:提供了 BeanFactory,是工厂模式的经典实现, Spring 将管理对象称为 Bean。
    • Core 核心模块:提供了 Spring 框架的基本组成部分,包括 loC 和 DI 功能。
    • Context 上下文模块: 建立在 Core 和 Beans 模块的基础之上,它是访问定义和配置的任 何对象的媒介。 其中 ApplicationContext 接口是上下文模块的焦点。
    • Context-support 模块:提供了对第三方库嵌入 Spring 应用的集成支持,比如缓存 ( EhCache、 Guava、 JCache)、 邮件服务 (JavaMail)、 任务调度( CommonJ、 Quartz) 和模 板引擎( FreeMarker、 JasperReports、 速率)。
    • SpEL 模块:是 Spring 3.0 后新增的模块,它提供了 Spring Expression Language 支持, 是运行时查询和操作对象图的强大的表达式语言。
  2. Data Access/lntegration (戴帽访问l集成)
    数据访问/集成层包括 JDBC、 ORM 、 OXM、 JMS 和 Transactions 模块,真体介绍如下。
    • JDBC 模块:提供了一个 JDBC 的抽象层,大幅度地减少了在开发过程中对数据库操作的 编码。
    • ORM 模块:对流行的对象关系映射 API ,包括 JPA、 JDO 和 Hibernate 提供了集成层支持。
    • OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、 Castor、 XMLBeans、 JiBX 和 XStream。
    • JMS 模块:指 Java 消息传递服务,包含使用和产生信息的特性,自 4. 1 版本后支持与 Spring-message 模块的集成。
    • Transactions 事务模块:支持对实现特殊接口以及所有 POJO 类的编程和声明式的事 务管理。
  3. Web
    Spring 的 Web 层包括 WebSocket、 Servlet、 Web 和 Portlet 模块,具体介绍如下。
    • WebSocket 模块: Spring 4.0 以后新增的模块,它提供了 WebSocket 和 SockJS 的实现, 以及对 STOMP 的支持。
    • Servlet 模块:也称为 Spring-webmvc 模块,包含了 Spring 的模型一视图一控制器(MVC) 和 REST Web Services 实现的 Web 应用程序。
    • Web 模块:提供了基本的 Web 开发集成特性,例如: 多文件上传功能、 使用 Servlet 监 听器来初始化 loC 容器以及 Web 应用上下文。
    • Portlet 模块:提供了在 Portlet 环境中使用 MVC 实现,类似 Servlet 模块的功能。
  4. 其他模块
    Spring 的其他模块还有 AOP、 Aspects、 Instrumentation 以及 Test 模块,具体介绍如下。
    • AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能 进行分离,以降低搞合性。
    • Aspects 模块:提供了与 AspectJ 的集成功能, AspectJ 是一个功能强大且成熟的面向切 面编程 (AOP) 框架。
    • Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器 中使用。
    • Messaging 模块: Spring 4.0 以后新增的模块,它提供了对消息传递体系结构和协议 的支持。
    • Test 模块:提供了对单元测试和集成测试的支持。
  • Spring 的下载及目录结构

Spring 的第一个版本是在 2004 年发布的,经过 10 多年的发展, Spring 的版本也在不断地 升级优化中。我的代码都是基于Spring 4.3.6编写实现的, 建议大家也下载该版本。 Spring 开发所需的 JAR 包分为两个部分,具体如下。

  1. Spring 框架包 Spring 4.3.6 版本的框架压缩包,名称为 spring-framework-4.3.6.RELEASE-dist.zip ,此压缩包可以通过地址 ''https://repo.spring.io/release/org/springframework/spring/4.3.6.RELEASE/" 下载。 下载完成后,将压缩包解压到自定义的文件夹中,解压后的文件目录结 构如图所示。 在图的目录中, docs 文件夹中包含 Spring 的 API 文档和开发规范j libs 文件夹中包含开发需要的 JAR 包和源码 i schema 文件夹中包含开发所需要的 schema 文件,这些文件中定 义了 Spring 相关配置文件的约束。

    打开 libs 目录可以看到 60 个 JAR 文件,如下图所示。

    从图中可以看出. libs 目录中的 JAR 包分为三类,其中以 RELEASE.jar 结尾的是 Spring 框架 class 文件的 JAR 包;以 RELEASE - javadoc. jar 结尾的是 Spring 框架 API 文档的压缩包; 以 RELEASE-sources.jar 结尾的是 Spring 框架源文件的压缩包。 整个 Spring 框架由 20 个模块 组成,该目录下 Spring 为每个模块都提供了这三类压缩包。
    在 libs 目录中,有四个 Spring 的基础包,它们分别对应 Spring 核心容器的四个模块,具体 介绍如下。
    • spring-core-4.3.6.RELEASE.jar: 包含 Spring 框架基本的核心工具类, Spring 其他组件 都要用到这个包里的类,是其他组件的基本核心。
    • spring-beans-4.3.6.RELEASE.jar: 所有应用都要用到的 JAR 包,它包含访问配置文件、 创建和管理 Bean 以及进行 Inversion of Control(loC)或者 Dependency Injection(DI)操作相关的 所有类。
    • spring-context -4.3.6.RELEASE.jar: Spring 提供了在基础 loC 功能上的扩展服务,还提 供了许多企业级服务的支持,如邮件服务、任务调度、 JNDI 定位、 EJB 集咸、远程访问、缓存 以及各种视图层框架的封装等。
    • spring-expression-4.3.6.RELEASE.jar: 定义了 Spring 的表达式语言。
  2. 第三万依赖包
    在使用 Spring 开发时,除了要使用自带的 JAR 包外, Spring 的核心容器还需要依赖 commons.logging 的 JAR 包。 该 JAR 包可以通过网址 "http://commons.apache.org/proper/commons-logging/download_logging.cgi
    " 下载。 下载完成后,会得到一个名为 commonslogging-1 .2-bin.zit 的压缩包。 将压缩包解压到自定义目录后,即可找到 commons-Iogging1 .2.jar。
    初学者学习 Spring 框架时,只需将 Spring 的 4 个基础包以及 commons-logging-1.2.jar 复制到项目的 lib 目录,并发布到类路径中即可。
Spring 的核心容器

Spring 框架的主要功能是通过其核心容器来实现的,因此在正式学习 Spring 框架的使用之 前,有必要先对其核心容器有一定的了解。Spring 框架提供了两种核心容器,分别为 BeanFactory 和 ApPI icationContext。 本节中将对这两种核心容器进行简单的介绍。

  • BeanFactory

BeanFactory 由 org.springframework.beans.facytory.BeanFactory 接口定义,是基础类型 的 loC 容器(关于 loC 的具体含义将在后面的小节讲解,这里只需知道其表示控制反转), 它提供了完整的 loC 服务支持。 简单来说, BeanFactory就是一个管理 Bean 的工厂,它主要负 责初始化各种 Bean ,并调用它们的生命周期方法。
BeanFactory 接口提供了几个实现类,其中最常用的是 org.springframework. beans factory.xml.XmIBeanFactory ,该类会根据 XML 配置文件中的定义来装配 Bean。
创建 BeanFactory 实例时,需要提供 Spring 所管理容器的详细配置信息,这些信息通常采 用 XML 文件形式来管理,其加载配置信息的语法如下。

BeanFactory beanFactory = 
new XmlBeanFactory(new FileSystemResource("F: /applicationContext.xml")) ; 

这种加载方式在实际开发中并不多用,大家了解即可。

  • ApplicationContext

ApplicationContext是 BeanFactory 的子接口,也被称为应用上下文,是另一种常用的 Spring 核心容器。 它由 org.springframework.context. ApplicationContext 接口定义,不仅包含了 BeanFactory 的所有功能,还添加了对国际化、资源访问、事件传播等方面的支持。
创建 ApplicationContext 接口实例,通常采用两种方法,具体如下。

  1. 通过 ClassPathXmlApplicationContext
    创建 ClassPathXmlAp~licationContext 会从类路径 classPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,其使用语法如下。
ApplicationContext applicationContext = 
new ClassPathXmlApplicationContext(String configLocation) ; 

上述代码中, configLocation 参数用于指定 Spring 配置文件的名称和位置。 如果其值为 "applicationContext.xml" ,则 Spring 会去类路径中查找名称为 applicationContext.xml 的配置 文件。

  1. 通过 FileSystemXmlApplicationContext
    创建 FileSystemXmlApplicationContext 会从指定的文件系统路径(绝对路径)中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,其使用语法如下。
ApplicationContext applicationContext = 
new FileSystemXmlApplicationContext(String configLocation); 

与 ClassPathXmlApplicationContext 有所不同的是,在读取 Spring 的配置文件时, FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件 的位置,例如 "D:/workspaces/appIicationContext. xml"。 如果在参数中写的不是绝对路径,那 么方法调用的时候,会默认用绝对路径来找。 这种采用绝对路径的方式,会导致程序的灵活性变差,所以这个方法一般不推荐使用。
在使用 Spring 框架时,可以通过实例化其中任何一个类来创建 ApplicationContext 容器。 通常在 Java 项目中,会采用通过 ClassPathXmlApplicationContext 类来实例化 ApplicationContext 容器的方式,而在 Web 项目中, ApplicationContext 容器的实例化工作会交由 Web 服务器来完成 。 Web 服务器实例化 ApplicationContext 容器时,通常会使用基于 ContextLoaderListener 实现的方式,此种方式只需要在 web.xml 中添加如下代码。

<!-- 指定 Spring 配置文件的位置,多个配置文件时,以逗号分隔 --> 
<context-param> 
  <param-name>contextConfigLocation</param-name>
  <!-- Spring 将加载 spring 目录下的 applicationContext .xml 文件 -->
  <param-value> classpath:spring/applicationContext.xml </param-value>
</context-param> 
<!-- 指定以 ContextLoaderListener 方式启动 Spring 容器 -->
<listener>
  <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> 
</listener> 

在后面章节讲解三大框架整合以及项目时,将采用基于 ContextLoaderListener 的方式 由 Web 服务器实例化 ApplicationContext 容器。
创建 Spring 容器后,就可以获取 Spring 容器中的 Beano Spring 获取 Bean 的实例通常采 用以下两种方法。
• Object getBean(String name): 根据容器中 Bean 的 id 或 name 来获取指定的 Bean ,获 取之后需要进行强制类型转换。
• <T> T getBean(Class<T> requiredType) : 根据类的类型来获取 Bean 的实例。 由于此方 法为泛型方法,因此在获取 Bean 之后不需要进行强制类型转换。

  • 小提示
    BeanFactory 和 ApplicationContext 两种容器都是通过 XML 配置文件加载 Bean 的。 二者的主 要区别在于,如果 Bean 的某一个属性没有注入,使用 BeanFacotry JJn载后,在第一次调用 getBean()方法时会抛出异常,而 ApplicationContext 则在初始化时自检,这样有利于检查所依赖 属性是否注入。 因此 ,在实际开发中,通常都优先选择使用 ApplicationContext ,而只有在系统 资源较少时,才考虑使用 BeanFactory。
Spring的入门程序

通过上一小节的学习,相信读者对Spring的核心容器已经有了一个初步的了解。为了帮助大家快速地学习Spring,下面通过一个简单的入门程序来演示Spring框架的使用。

  • (1)在Eclipse中,创建一个名为spring01的Web项目,将Spring的4个基础包以及commons-Iogging的JAR包复制到lib目录中,并发布到类路径下,如下图所示。


  • (2)在src目录,创建一个com.neuedu.ioc包,并在包中创建接口UserDao,然后在接口中定义一个say()方法,如下面文件所示。
package com.neuedu.ioc;

public interface UserDao {
  public void say(); 

}
  • (3)在com.neuedu.ioc包下,创建UserDao接口的实现类UserDaolmpl,该类需要实现接口中的say()方法,并在方法中编写一条输出语句,如下面文件所示。
package com.neuedu.ioc;

public class UserDaoImpl implements UserDao {

  @Override
  public void say() {
      System.out.println("userDao say hello World !");
  }
}
  • (4)在src目录下,创建Spring的配置文件applicationContext.xml,并在配置文件中创建一个id为userDao的Bean,如下面文件所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <!-- 将指定类配置给 Spring,让 Spring 创建其对象的实例 --> 
   <bean id="userDao" class="com.neuedu.ioc.UserDaoImpl"></bean>
   
</beans>

在上述文件中,第2-5行代码是Spring的约束配置。该配置信息不需要大家手写,可以在Spring的帮助文档中找到参见本节中的多学一招)。第7行代码表示在Spring器中创建一个id为userDao的Bean实例,其中class属性用于指定需要实例化Bean的类。
注意:Spring配置文件的名称可以自定义,通常在实际开发中,applicationContext.xml(有时也会命名为beans.xml)。

  • (5)在com.neuedu.ioc包下,创建测试类TestloC,并在类中编写main()方法。在main()方法中,需要初始化Spring容器,并加载配置文件,然后通过Spring容器获取userDao实例(即Java对象),最后调用实例中的say()方法,如下面文件所示。
package com.neuedu.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestIoc {
  
  public static void main(String[] args) {
      //1.初始化 spring 容器,加载配置文件 
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      //2.通过容器获取 userDao 实例 
      UserDao userDao = (UserDao) applicationContext.getBean("userDao"); 
      //3.调用实例巾的 say()方法 
      userDao.say();

  }

}

行程序后,控制台的输出结果如下图所示。


从图上图可以看出,控制台已成功输出了UserDaolmpl类中的输出语句。在上面的文件main()方法中,并没有通过new关键字来创建UserDao接口的实现类对象,而是通过Spring容器来获取的实现类对象,这就是SpringloC容器的工作机制。
多学一招:快速获取配置文件的约束信息
在Spring的配直文件中,包含了很多约束信息,初学者如果自己动手去编写,不但浪费时间,还容易出错。其实,在Spring的帮助文档中,就可以找到这些约束信息,具体的获取方法下。
打开Spring解压文件夹中的docs目录,在spring-framework-reference文件夹下打开html文件夫,并找到index.html文件,如下图所示。

使用浏览器打开index.html后,在浏览器页面的OverviewofSpringFramework下的7.2.1小节Configurationmetadata中,即可找到配直文件的约束信息,如图所示.在图中,标记处的配直信息就是Spring配直文件的约束信息。初学者只需将标注处的信息复制到项目的配直文件中使用即可。此外,由于使用的是Spring4.3版本,所以还需要在复制后的xsd信息中加入版本号信息,其代码如下所示。

http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

学习时,大家可以直接复制相关的源代码。如果涉及配直文件的约束信息,可以将相应源代码中的配直文件约束信息复制过来直接使用。


依赖注入
  • 依赖注入的概念

依赖注入(DependencyInjection,简称DI)与控制反转(loC)的含义相同,只不过这两个称呼是从两个角度描述的同一个概念。对于一个Spring初学者来说,这两种称呼很难理解,下面我们将通过简单的语言来描述这两个概念。当某个Java对象(调用者)需要调用另一个Java对象(被调用者,即被依赖对象)时,在传统模式下,调用者通常会采用"new被调用者"的代码方式来创建对象,如下图所示。这种方式会导致调用者与被调用者之间的捐合性增加,不利于后期项目的升级和维护。



在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转。
从Spring容器的角度看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入,如下图所示。


  • 依赖注入的实现方式

依赖注入的作用就是在使用Spring框架创建对象时,动态地将其所依赖的对象注入Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入,具体介绍如下。

  • 属性setter方法注入:指loC容器使用setter方法注入被依赖的实例。通过调用无参构造器或无参静态工厂方法实例化Bean后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入。
  • 构造方法注入:指loC容器使用构造方法注入被依赖的实例。基于构造方法的依赖注入通过调用带参数的构造方法来实现,每个参数代表着一个依赖。

了解了两种注入方式后,下面以属性setter方法注入的方式为例,讲解一下Spring容器在应用中是如何实现依赖注入的。
(1)在com.neuedu.ioc包中,创建接口UserService,在接口中编写一个say()方法,如下面文件所示。

package com.neuedu.ioc;

public interface UserService {

  public void say();
}

(2)在com.neuedu.ioc包中,创建UserService接口的实现类UserServicelmpl,在类中声明userDao属性,并添加属性的setter方法,如下面文件所示。

package com.neuedu.ioc;

public class UserServicelmpl implements UserService {
  //声明userDao属性
  private UserDao userDao;
  //添加属性的setter方法
  public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
  }
  
  //实现的接口中的方法
  @Override
  public void say() {
      //调用UserDao中的say()方法,并执行输出语句
      this.userDao.say();
      System.out.println("userService say hello World !");

  }

}

(3)在配置文件appIicationContext.xmI中,创建一个id为userService的Bean,该Bean用于实例化UserServicelmpl类的信息,并将userDao的实例注入到userService中,其代码如下所示。

<!-- 添加一个 id 为 userService 的实例 --> 
   <bean id="userService" class="com.neuedu.ioc.UserDaoImpl">
      <!-- 将 id 为 userDao 的 Bean 实例注入到 userService 实例中 -->
      <property name="userDao" ref="userDao"></property>
   </bean>

上述代码中,<property>是<bean>元素的子元素,它用于调用Bean实例中的setUserDao()方法完成属性赋值,从而实现依赖注入。其name属性表示Bean实例中的相应属性名,ref属性用于指定其属性值。
(4)在com.neuedu.ioc包中,创建测试类TestDI,来对程序进行测试,编辑后如下面文件所示。

package com.neuedu.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestDI {

  public static void main(String[] args) {
      //1.初始化 spring 容器,加载配置文件
      ApplicationContext applicationContext =  new ClassPathXmlApplicationContext("applicationContext.xml"); 
      //2.通过容器获取 UserService 实例 
      UserService userService = (UserService)applicationContext.getBean("userService"); 
      //3. 调用实例中的 say() 方法 1
      userService.say();

  }

}

(5)执行程序后,控制台的输出结果如图所示。



从上图可以看出,使用Spring容器通过UserService实现类中的say()方法,调用了UserDao实现类中的say()方法,并输出了结果。这就是Spring容器属性setter注入的方式,也是实际开发中最为常用的一种方式。

  • 本章小结

本章主要讲解了Spring框架入门的一些基础知识。首先讲解了Spring框架的概念、作用、优点、体系结构以及如何下载和下载后的目录结构,然后介绍了Spring的两种核心容器。接下来通过一个入门程序来讲解如何使用Spring框架。最后详细讲解了依赖注入和控制反转的概念,并演示了依赖注入中setter方法注入的实现。通过本章的学习,大家可以对Spring框架及其体结构有一个初步的了解,能够初步地掌握Spring框架的使用,并能够理Spring框架中loC和DI的思想,掌握属性setter方法注入的实现。

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

推荐阅读更多精彩内容