Spring Framework 概览

翻译 Spring Framework Reference Documentation,有增删改
Spring 框架是一个轻量级的解决方案,可以一站式地构建企业级应用。Spring 是模块化的,开发者可以在项目中只使用其中自己需要的部分,例如,可以在任何 web 框架上使用控制反转(IoC),也可以只使用 Hibernate 集成代码或 JDBC 抽象层。它支持声明式事务管理、通过 RMI 或 web 服务实现远程访问,并可以使用多种方式持久化数据。它提供了功能全面的 MVC 框架 ,也可以透明地集成 AOP 到软件中。
Spring 是非侵入式的,这意味着项目的域逻辑代码通常不会依赖于框架本身。在集成层(比如数据访问层)可能会同时依赖于数据访问技术和 Spring,但是这些依赖可以很容易地从代码中分离出来。
Spring框架是基于 Java 平台的,为开发Java应用提供了全方位的基础设施支持,所以你只需要关注你的应用本身。
Spring可以使用 POJO(plain old Java object)创建应用,并且可以将企业服务非侵入式地应用到 POJO。这项功能适用于 Java SE 编程模型以及全部或部分的 Java EE
那么,Spring 框架对 JAVA 开发项目有那些帮助?

  • 不用关心事务 API 就可以执行数据库事务;
  • 不用关心远程 API 就可以使用远程操作;
  • 不用关心 JMX API 就可以进行管理操作;
  • 不用关心 JMS API 就可以进行消息处理。

一. Spring Framework 基本构成

Spring Framework 大致按功能划分为20个模块,并可分为 Core Container、Data Access/Integration、Web, AOP (Aspect Oriented Programming)、Instrumentation、Messaging以及 Test 这7组,如下图所示:


GroupId ArtifactId Description
org.springframework spring-aop 基于代理的AOP支持
org.springframework spring-aspects 基于AspectJ 的切面
org.springframework spring-beans Beans 支持,包含 Groovy
org.springframework spring-context 应用上下文运行时,包括调度和远程抽象
org.springframework spring-context-support 支持将常见的第三方类库集成到 Spring 应用上下文
org.springframework spring-core Spring 其他模块所依赖的核心模块
org.springframework spring-expression Spring 表达式语言,SpEL
org.springframework spring-instrument JVM 引导的仪表(监测器)代理
org.springframework spring-instrument-tomcat Tomcat 的仪表(监测器)代理
org.springframework spring-jdbc JDBC(Java Data Base Connectivity) 支持包,包括数据源设置和 JDBC 访问支持
org.springframework spring-jms JMS(Java Message Service) 支持包,包括发送/接收JMS消息的助手类
org.springframework spring-messaging 对消息架构和协议的支持
org.springframework spring-orm 对象/关系映射(Object/Relational Mapping,ORM),包括对 JPA 和 Hibernate 的支持
org.springframework spring-oxm 对象/XML 映射(Object/XML Mapping,OXM)
org.springframework spring-test 单元测试和集成测试支持组件
org.springframework spring-tx 事务基础组件,包括对 DAO 的支持及 JCA 的集成
org.springframework spring-web web支持包,包括客户端及web远程调用
org.springframework spring-webmvc REST web 服务及 web 应用的 MVC 实现
org.springframework spring-webmvc-portlet 用于 Portlet 环境的MVC实现
org.springframework spring-websocket WebSocket 和 SockJS 实现,包括对 STOMP 的支持

以下章节简要说明各部分内容。

1.1 Core Container - 核心容器

Spring Core Container 包含 spring-core, spring-beans, spring-context, spring-context-support, 以及 spring-expression (Spring Expression Language,Spring 表达式语言,SpEL)模块。
其中,spring-core 和 spring-beans 模块提供了 Spring 框架 的基础功能,包括控制反转(IoC)和依赖注入。BeanFactory 是工厂模式的一种复杂而精致的实现,它使开发者不再需要编码式单例(programmatic singletons),并可将配置和依赖从实际编码逻辑中解耦。
spring-context 模块使得由 Core 和 Beans 提供的基础功能真正构建成坚实的地基:这意味着Spring 工程能以框架模式访问对象,类似于JNDI注册表。Context 模块继承了Beans 模块的特性并增加了对国际化(例如资源绑定)、事件传播、资源加载和context 透明化(例如 Servlet container)。同时,也支持JAVA EE 特性,例如 EJB、 JMX 和 基本的远程访问。Context 模块的关键是 ApplicationContext 接口。spring-context-support 则提供了对第三方库集成到 Spring-context 的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression 模块为在运行时查询和操作对象图提供了强大的表达式语言。它是JSP2.1规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。

1.2 Data Access/Integration - 数据访问与集成

数据访问与集成层包含 JDBC、ORM、OXM、JMS和事务模块。
spring-jdbc 模块提供了 JDBC抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
spring-tx 模块支持编程式事务和声明式事务,可用于实现了特定接口的类和所有的 POJO 对象。编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细。
spring-orm 模块提供了对流行的对象关系映射 API的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring 的其它功能整合,比如前面提及的事务管理。
spring-oxm 模块提供了对 OXM 实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
spring-jms 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了 spring-messaging 模块。

1.3 AOP

spring-aop 模块提供了面向切面编程(AOP)的实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦。使用源码级的元数据,可以用类似于.Net 属性的方式合并行为信息到代码中。
spring-aspects 模块提供了对 AspectJ 的集成。

1.4 Instrumentation

spring-instrument 模块提供了对检测类的支持和用于特定的应用服务器的类加载器的实现。
spring-instrument-tomcat 模块包含了用于 Tomcat 的Spring 检测代理。

1.5 Messaging - 消息处理

从 Spring 4 开始集成了spring-messaging 模块,是从一些 Spring 集成项目的关键抽象中提取出来的,这些项目包括 Message、MessageChannel、MessageHandler 和其它服务于消息处理的项目。这个模块也包含一系列的注解用于映射消息到方法,这类似于Spring MVC 基于编码模型的注解。

1.6 Web

Web 层包括 spring-web、spring-webmvc、spring-websocket、spring-webmvc-portlet 等模块。
spring-web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如 multipart 文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
spring-webmvc 模块(即 Web-Servlet 模块)为 web 应用提供了模型视图控制(MVC)和 REST Web 服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
spring-webmvc-portlet 模块(即Web-Portlet模块)提供了用于 Portlet 环境的 MVC 实现,并反映了 pring-webmvc 模块的功能。

1.7 Test

spring-test 模块通过 JUnit 和 TestNG 组件支持单元测试和集成测试。它提供了一致性地加载和缓存 Spring 上下文,也提供了用于单独测试代码的模拟对象(mock object)。

二. 使用场景

不管是资源受限的嵌入式应用还是使用了事务管理和 web 集成框架的成熟的企业级应用,Spring 都是一个值得信赖的选择。

2.1 经典的 Spring web 应用

Spring 的声明式事务管理可以使 web 应用完全事务化,就像使用 EJB 容器管理的事务。所有订制的业务逻辑都可用 POJO 实现,并用 Spring 的 IoC 容器进行管理。此外还包括发邮件和验证功能,其中验证功能是从 web 层分离的,由你决定何处执行验证。Spring 的 ORM 可以集成 JPA、hibernate和 JDO 等。比如使用 Hibernate 时,可以继续使用已存在的映射文件和标准的 Hibernate SessionFactory 配置。表单控制器无缝地把 web 层和领域模型集成在一起,移除了 ActionForms 和其它把 HTTP 参数转换成领域模型的类。


2.2 使用第三方 web 框架和 Spring 中间件

一些场景可能不允许你完全切换到另一个框架。然而,Spring 框架不强制你使用它所有的东西,它不是非此即彼(all-or-nothing)的解决方案。前端使用 Struts、Tapestry、JSF 或别的 UI 框架可以和 Spring 中间件集成,从而使用 Spring 的事务管理功能。仅仅只需要使用 ApplicationContext 连接业务逻辑,并使用 WebApplicationContext 集成 web 层即可。

2.3 远程调用使用场景

当需要通过 web 服务访问现有代码时,可以使用Spring的Hessian-,Burlap-,Rmi-或者JaxRpcProxyFactory类,远程访问现有的应用并非难事。

2.4 EJBs - 包装现有的POJO

Spring框架也为 EJB 提供了访问抽象层,可以重新使用现有的 POJO 并把它们包装到无状态的会话 bean 中,以使其用于可扩展的安全的 web 应用中。


三. 依赖管理

依赖管理和依赖注入是两码事。为了让应用程序拥有 Spring 的这些非常棒的功能(如依赖注入),需要在工程中导入所需的jar 包,并在运行时放在classpath 下,有可能的话编译期也应该放在 classpath 下。这些依赖并不是被注入的虚拟组件,而是文件系统上实在的物理资源。依赖管理的处理过程涉及到定位这些资源、存储并添加它们到 classpath 下。依赖可能是直接的(比如运行时依赖于 Spring),也可能是间接的(比如依赖于 commons-dbcp,commons-dbcp 又依赖于 commons-pool)。间接的依赖又被称作“传递”,它们是最难识别和管理的。
如果准备使用 Spring,则需要拷贝一份所需 Spring 模块的 jar 包。为了便于使用,Spring 被打包成一系列的模块以尽可能地减少依赖,比如,如果不是在写一个web应用,那就没必要引入 spring-web 模块。
每个版本的Spring都会在以下地方发布artifact:

  • Maven中央仓库,默认的 Maven 查询仓库,并且不需要特殊的配置就可以使用。许多 Spring 依赖的公共库也可以从 Maven 中央仓库获得,并且大部分的 Spring 社区也使用 Maven 作为依赖管理工具,所以很方便。Maven中的jar包命名格式为spring-*-<version>.jar,其groupId是org.springframework。
  • 特别托管 Spring 的公共 Maven 仓库。除了最终的 GA 版本,这个仓库也托管了开发的快照版本和里程碑版本。jar 包和 Maven 中央仓库中的命名一致,所以这也是一个获取 Spring 的开发版本的有用地方,可以和 Maven 中央仓库中部署的其它库一起使用。这个仓库也包含了所有 Spring jar 包的发行版的zip文件,以便于下载。

因此,首先要做的事就是决定如何管理依赖关系,我们一般推荐使用成熟的依赖和构建生命周期管理系统,比如 Maven、Gradle 或 Ivy,当然你也可以手动下载所有的jar包。
Spring 对大部分企业和其它外部工具提供了集成和支持,把强制性的外部依赖降到了最低,这样就不需要为了简单地使用 Spring 而去寻找和下载大量的 jar 包了。基本的依赖注入只有一个强制性的外部依赖,那就是日志管理(参考下面关于日志管理选项的详细描述)。
下面列出依赖于 Spring 的应用的基本配置步骤,首先使用 Maven,然后 Gradle,最后 Ivy。在所有案例中,如果有什么不清楚的地方,参考所用的依赖管理系统的文档或查看一些范例代码—— Spring 构建时本身使用 Gradle 管理依赖,所以我们的范例大部分使用 Gradle 或 Maven。

3.1 使用 Maven 管理依赖

如果使用Maven作为依赖管理工具,甚至不需要明确地提供日志管理的依赖。例如,创建应用上下文并使用依赖注入配置应用程序,Maven的依赖关系看起来像下面一样:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies></pre>

就这样,注意如果不需要编译Spring API,可以把scope声明为runtime,这是依赖注入使用的典型案例。
上面的示例使用Maven中央仓库,如果使用Spring的Maven仓库(例如,里程碑或开发快照),需要在Maven配置中指定仓库位置。
release版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.release</id>
<url>http://repo.spring.io/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>

里程碑版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.milestone</id>
<url>http://repo.spring.io/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>

快照版本:

<pre><repositories>
<repository>
<id>io.spring.repo.maven.snapshot</id>
<url>http://repo.spring.io/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories></pre>

使用 Maven 时可能会不小心混合了 Spring 不同版本的 jar 包。例如,你可能会发现第三方库或其它的 Spring 项目存在旧版本的传递依赖。如果没有明确地声明直接依赖,各种各样不可预料的情况将会出现。
为了解决这个问题,Maven 提出了“物料清单式”(Bill Of Materials,BOM)依赖的概念。可以在 dependencyManagement 部分导入 spring-framework-bom 以保证所有的 Spring 依赖(不管直接还是传递依赖)使用相同的版本。
<pre><dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement></pre>

使用 BOM 的另外一个好处是不再需要指定 <version> 属性了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies></pre>

3.2 使用 Gradle 管理依赖

为了在 Gradle 构建系统中使用 Spring 仓库,需要在 repositories 部分包含合适的 URL:
<pre>repositories {
mavenCentral()
// and optionally...
maven { url "http://repo.spring.io/release" }
}
</pre>

可以酌情把repositories URL中的/release修改为/milestone/snapshot。一旦仓库配置好了,就可以按Gradle的方式声明依赖关系了。
<pre>dependencies {
compile("org.springframework:spring-context:4.3.9.RELEASE")
testCompile("org.springframework:spring-test:4.3.9.RELEASE")
}</pre>

3.3 使用 Ivy 管理依赖

使用 Ivy 管理依赖关系有相似的配置选项。
ivysettings.xml 中添加 resolver 配置使 Ivy 指向 Spring 仓库:
<pre><resolvers>
<ibiblio name="io.spring.repo.maven.release"
m2compatible="true"
root="http://repo.spring.io/release/"/>
</resolvers></pre>

可以酌情把root URL中的/release修改为/milestone或/snapshot。一旦配置好了,就可以按照惯例添加依赖了(在ivy.xml中):
<pre><dependency org="org.springframework"
name="spring-core" rev="4.3.9.RELEASE" conf="compile->runtime"/></pre>

四. 日志

对于 Spring 来说日志管理是非常重要的依赖关系,因为:

  • 它是唯一的强制性外部依赖;
  • 每个人都喜欢从他们使用的工具看到一些输出;
  • Spring 集成了许多其它工具,它们都各自选择了日志管理的依赖。

开发者通常在整个应用程序(包括所有的外部组件)的中心位置统一配置日志管理,这是非常困难的,因为现在有很多日志管理的框架可供选择。
Spring 中强制的日志管理依赖是 Jakarta Commons Logging API(JCL)。我们编译了 JCL,并使 JCL 的 Log 对象对继承了 Spring 框架的类可见。对用户来说所有版本的 Spring 使用相同的日志管理库很重要,因为这使得迁移很简单,因为Spring保存了向后兼容,即使对于扩展了 Spring 的应用也能向后兼容。
这是怎么做到的呢?我们让 Spring 的一个模块明确地依赖于 commons-logging(JCL的典型实现),然后让所有其它模块都在编译期依赖于这个模块。例如,使用 Maven,你想知道哪里依赖了 commons-logging,其实是 Spring 确切地说是其核心模块 spring-core 依赖了。
commons-logging 的优点是不需要其它任何东西就可以使应用程序运转起来。它拥有一个运行时发现算法,用于在 classpath 中寻找其它日志管理框架并且适当地选择一个使用(或者告诉它使用哪个)。如果不需要其它的功能了,你就可以从 JDK(java.util.logging 或 JUL)得到一份看起来很漂亮的日志了。你会发现大多数情况下你的 Spring 应用程序工作得很好且日志很好地输出到了控制台,这很重要。

4.1 禁用 Commons Logging

不幸的是,commons-logging 的运行时发现算法虽然对于终端用户很方便,但存在一定的问题。有两种方式关掉 commons-logging

  • spring-core 模块中去除对 commons-logging 的依赖(因为这是唯一明确依赖于 commons-logging 的地方)
  • 依赖于一个特定的 commons-logging 且把其 jar 包换成一个空 jar 包(具体做法参考SLF4J FAQ

如下,在 dependencyManagement 中添加部分代码就可以排除掉 commons-logging 了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies></pre>

现在这个应用可能是残缺的,因为在classpath上没有 JCL API 的实现,所以需要提供一个新的去修复它。

4.2 使用 Log4j

许多人使用 Log4j 作为日志管理框架。它是高效和完善的,实际上在构建和测试 Spring时我们使用的就是它。Spring 也提供了一些工具用于配置和初始化 Log4j,所以某些模块在编译期也可以选择依赖于 Log4j。
为了使用 Log4j 2.x,开发者需要拷贝相应版本的 Log4j 到 classpath 并提供相应的配置文档(log4j2.xml、log4j2.properties 或 其他支持的配置格式)。对于 Maven 项目而言,最小依赖如下所示:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>

如果你也想将 SLF4J 日志委托给 Log4j,例如在项目依赖的其他库默认使用 SLF4J 的情况下,还需要以下依赖:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>

以下是使用 Logfj2.xml 配置输出日志到 console 的例子:
<pre><?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework.beans.factory" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration></pre>

4.3 使用 SLF4J 与 Log4j 或 Logback

SLF4J(Simple Logging Facade for Java )是 Spring 中其他模块经常采用的 API,它通常与 Logback 一块儿使用。后者是 SLF4J API 的一种本地化实现。
SLF4J 提供了与多种其他常见日志框架的绑定桥梁以委托日志输出,包括Log4j。开发者可以决定将SLF4J管理的日志委托给Log4j输出,或者反过来。为了使用 SLF4J,需要在依赖中将 commons-logging 替换成 SLF4J-JCL 桥。此后 Spring 内部的日志调用将转换为调用 SLF4J API,如果应用中的其他依赖也使用了 SLF4J,那么日志就能在一个地方统一管理了。
一个常用的选择是将 Spring 桥接到 SLF4J 后 再显式地将 SLF4J 绑定到 Log4j,那么需要提供以下依赖并排除 commons-logging:JCL 桥、SLF4J到Log4j 的绑定、以及 Log4j 本身。
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies></pre>

另一个常见选择是直接绑定到 Logback。这可以移除其他额外步骤和依赖,因为 Logback 直接实现了 SLF4J API:
<pre><dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies></pre>

4.4 使用 JUL (java.util.logging)

如果 classpath 目录下没有Log4j,commons-logging 将默认委托给 java.util.logging,所以在这种情况下无需任何其他配置。

4.5 带有本地 JCL 的运行时容器

许多人在一个容器中运行 Spring 应用程序,而这个容器本身又提供了JCL的实现,例如 IBM 的 Websphere Application Server(WAS)。这本身没什么问题,但要考虑到以下两种不同场景:

  • 在 “parent first” 类加载委托模型(WAS即默认如此)下,应用通常选用父容器提供的 commons-logging 版本,委托给 WAS 日志子系统(这个子系统实际上是基于 JUL 的)。而应用本身提供的 JCL 变体(不管是 Spring 默认的 commons-logging 版本还是JCL-over-SLF4J 桥)则被忽略,应用所依赖的其他库所默认的 日志管理框架也同样会被完全忽略。
  • 在“parent last” 委托模型(在常规的Servlet容器中既如此,WAS中则需要显式配置)中,应用会默认选用自身配置的日志管理框架。如果本身没有特别配置,则和 “parent first” 场景相同。

总而言之,我们推荐采用 “parent last” 场景。

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

推荐阅读更多精彩内容