Maven POM 基础配置

The Basics

      POM文件包含关于项目的所有必要信息,以及在构建项目过程中使用的插件配置。
      它是一个对于"who","what","where"的声明性表现,而build生命周期是一个"when"和"how"的表现。但这并不是说POM文件不能影响生命周期的流程,它是可以的。比如通过maven-antrun-plugin配置它可以有效的将Apache Ant任务嵌入到POM中,然而它只是一个最终的声明。build.xml会在Ant运行时告诉它做什么(过程中),pom只是声明它的配置(声明)。对于pom来说,如果某些外力导致生命周期跳过Ant插件,它是不会停止那些执行中的插件的;而build.xml的任务总是依赖于之前执行的任务。

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>

Maven坐标

      groupId,artifactId,version都是必填项(如果从父类继承groupId和version,则不需要显式地定义它们)。这三个字段很像一个地址和时间戳,它们在存储库中标记了一个特定的位置,充当Maven项目的坐标系统。

  • groupId:它在组织或项目中通常是唯一的。

      groupId并不是必须使用"."分隔(如junit),另外使用"."标记的groupId也不一定要与项目的包结构相对应(不过建议与包结构对应)。当在存储仓库中存储时,group的行为和在操作系统中的Java包结构非常相似。这些"."被操作系统特定的目录分隔符替换(如UNIX中的"/")使基础仓库变成了相对的目录结构。在上面的例子中org.codehaus.mojo组被存储于$M2_REPO/org/codehaus/mojo目录下。

  • artifactId:它通常是项目已知的名称。

      artifactId和groupId一起创建了一个key值,用来区别于世界上其他的项目。它与groupId一起定义了artifact在存储仓库中的位置。在上面的例子中my-project存储于$M2_REPO/org/codehaus/mojo/my-project目录。

  • version:用来指定项目的版本。

      groupId和artifactId可以表示单个项目,但它们无法描述我们正在讨论的是该项目的哪一种形式。比如我们是想使用2018年的junit:junit(4.12版本)还是2007年的junit:junit(3.8.2版本)?简而言之:代码更改,应该对这些更改进行版本控制,并且此元素可以使这些版本保持一致。同样的,它也被用于存储仓库中用来区分不同的版本。在上面的例子中my-project最终存储于$M2_REPO/org/codehaus/mojo/my-project/1.0目录中。

packaging

      用来声明项目的打包方式。若没有声明该部分则默认打包方式为jar。当前packaging的值有:pom,jar,maven-plugin,ejb,war,ear,rar

POM依赖关系

      Maven一个强大的方面是它对项目关系的处理,包括依赖项和传递依赖项、继承和聚合(多model的项目)。依赖项管理的传统就是除了最琐碎的项目之外,其他项目都是一团糟。随着依赖树变得庞大而复杂,"Jarmageddon"很快就出现了。接下来是"Jar Hell",在Jar Hell中,一个系统上依赖的版本与使用它开发的版本是不相等的,要么是给出的版本是错误的,要么是名称类似的Jar之间的版本冲突。Maven通过一个公共的本地存储库解决了这两个问题,从这个存储库可以正确地链接项目、版本等。

Dependencies

      POM的基础是其dependency列表。大多数项目依赖于其他项目才能成功build和运行。Maven会在编译时下载和链接这些dependency和它们依赖的其他目标。

  • Maven优点:帮助你管理所依赖项目的相关依赖,使你只需要关注自己项目所需的依赖关系即可。
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>

    <dependency> 
        <!-- 依赖的group ID --> 
        <groupId>org.apache.maven</groupId> 

        <!-- 依赖的artifact ID --> 
        <artifactId>maven-artifact</artifactId> 

        <!-- 依赖的版本号 --> 
        <version>3.8.1</version> 

        <!-- 依赖类型,默认类型是jar。
             它通常表示依赖的文件的扩展名,但也可以被映射成另外一个扩展名或分类器。
             type经常和使用的打包方式对应(也有例外)。一些类型的例子:jar,war,ejb-client和test-jar。
             如果设置extensions为true,就可以在plugin里定义新的类型。 --> 
        <type>jar</type> 

        <!-- 依赖的分类器。
             分类器可以区分属于同一个POM,但不同构建方式的artifact。分类器名被附加到version后面。
             例如,如果你想要构建两个单独的artifact成JAR,一个使用Java1.4编译器,另一个使用Java6编译器,
             就可以使用分类器来生成两个单独的JAR构件。 --> 
        <classifier></classifier> 

        <!-- 依赖范围。在项目发布过程中,帮助决定哪些构件被包括进来。欲知详情请参考依赖机制。 
            - compile :默认范围,用于编译 
            - provided:类似于编译,但支持你期待jdk或者容器提供,类似于classpath 
            - runtime: 在执行时需要使用 
            - test: 用于test任务时使用 
            - system: 需要外在提供相应的元素。通过systemPath来取得 
            - systemPath: 仅用于范围为system。提供相应的路径 
            - optional: 当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用 --> 
        <scope>test</scope> 

        <!-- 仅供system范围使用。
             注意,不鼓励使用这个元素,并且在新的版本中该元素可能被覆盖掉。
             该元素为依赖规定了文件系统上的路径,必须是绝对路径。
             推荐使用属性匹配绝对路径,如${java.home}. --> 
        <systemPath></systemPath> 

        <!-- 当计算传递依赖时,从依赖构件列表里,列出被排除的依赖构件集。
             即告诉maven你只依赖指定的项目,不依赖项目的依赖。此元素主要用于解决版本冲突问题 --> 
        <exclusions> 
            <exclusion> 
                <artifactId>spring-core</artifactId> 
                <groupId>org.springframework</groupId> 
            </exclusion> 
        </exclusions> 

        <!-- 可选依赖,如果你在项目B中把C依赖声明为可选,就需要在依赖于B的项目中显式的引用对C的依赖。
             可选依赖阻断依赖的传递性。 --> 
        <optional>true</optional> 
    </dependency> 
    
    ...
  </dependencies>
  ...
</project>
  • groupId,artifactId,version:

      此三元组用于及时计算特定项目的Maven坐标,并将其划分为该项目的依赖项。这个计算的目的是选择一个匹配所有依赖声明的版本(因为依赖是传递的,对于同一个artifact可能有多个dependency声明)。groupId & artifactId:直接指向此依赖的坐标;version:依赖项版本必要说明,用于计算依赖项的有效版本。
      由于依赖性是由Maven坐标描述的,所以项目只能依赖于Maven的artifact工作,这迫使你只能依赖那些Maven可以管理的dependency。
      有时候无法从Maven中央仓库下载一个项目,比如一个项目依赖了一个具有闭源许可证的JAR包,闭源的JAR包Maven中央仓库是不会存储的。有三种方法来处理这种情况:

  1. 使用install插件,在本地install这些依赖(此方法是最简单的推荐方法)。例子:
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar

注意:此处仍然需要一个地址。只有第一次需要使用命令行,然后install插件会根据给定的地址创建一个POM,此后可以直接引用。

  1. 使用自己的仓库并将其部署在里面。

      这是使用企业内部网络的公司最喜欢的一种方式,需要使每个人保持同步。此处使用的Maven命令为deploy:deploy-file,它与install:install-file使用方式类似

  1. 设置这个依赖的scope为system并且定义一个systemPath。但是此方式并推荐,主要为了引出以下属性:
  • classifier-依赖的分类器:

      classifier用于区分从同一个POM构建的但内容不同的artifact。它是一些可选的任意字符串,如果存在则被附加到artifact的版本号之后。
      例如:一个项目提供了一个针对JRE1.5的artifact,但同时也仍然支持JRE1.4的artifact。这时就可以使用classifier标记一个artifact为jdk15,另一个为jdk14,这样使用者就可以选择使用哪一个。另外一个应用场景就是将次要的artifact添加到项目的主artifact上。如果浏览Maven中央仓库就会注意到classifier"sources"和"javadoc"被用于部署项目源代码和API文档以及打包的类文件。

  • type-依赖的类型:

      对应于依赖的类型,默认为jar。它通常表示dependency文件的拓展名,也可以将type映射到不同的extension和classifier。type的值通常对应于packaging使用的值,但是也有例外,如:jar,ejb-client和test-jar,详情参见:http://maven.apache.org/ref/current/maven-core/artifact-handlers.html
PS:新的type可以在extensions为true的plugins中定义。

  • scope-依赖的作用域:
    此属性指的是手头任务(编译、运行和测试等)的类路径,以及如何限制依赖的传递性。有五个值可用:

    • compile:默认作用范围,当未指定时默认使用该值。编译期依赖可以在所有的类路径中使用,并且这些依赖关系可以被传播到其他项目。
    • provided:它与compile很相似,但是表示只希望在JDK或容器在运行时提供该依赖。它只在编译和测试的类路径上有效,并且是不传递的。
    • runtime:该作用域表示编译期不需要该dependency,但是执行时需要。它只在运行时和测试的类路径上有效,编译的类路径中不存在。
    • test:该作用域表示该dependency正常情况下是非必须的。它只在测试编译和执行阶段生效,是不传递的。
    • system:该作用域与provided类似,不过system表示需要显式的提供它包含的JAR包。该artifact总是可用的,但是不是从存储仓库中查找的。
  • systemPath-依赖的系统路径:

      当且仅当dependency的scope值为system时有效。如果scope的值不是system而设置了systemPath那么将build失败。该值指向的路径必须是绝对路径,因此建议使用属性来指定特定于机器的路径如 ${java.home}/lib。因为假定system作用域的依赖已经被安装了,所以Maven不会在存储仓库中检查该项目,而是检查该文件是否存在,如果不存在则Maven将build失败并且建议手动下载和安装它。

  • optional-依赖的可选项:

      当不可能(无论出于何种原因)将项目分割为子模块时,将使用optional依赖项。其思想是,一些依赖项仅用于项目中的某些功能,如果不使用该功能,就不需要这些依赖项。理想情况下,这样的功能应该划分为依赖于核心功能项目的子模块,这个新的子项目将只有非可选的dependency。然而,由于项目不能被分割(无论出于什么原因),这些依赖项被声明为可选的。如果用户希望使用与可选依赖项相关的功能,则必须在自己的项目中重新声明该可选依赖项。这不是处理这种情况的最好方法,但是可选的依赖项(optional)和依赖项排除(exclusion)都是权宜之计。
      应用场景:optional依赖项可以节省空间和内存,另外它们可以防止有问题的jar(违反许可协议或导致类路径问题)被绑定到WAR、EAR、fat jar或类似的jar中。
      optional的值只支持true或者false

  • exclusions-依赖的排除:

      exclusions指定了一个依赖项,它告诉Maven不要将其包含在当前依赖项中(这是由于依赖的传递性)。比如maven-embedder需要使用maven-core但是我们不希望使用它或者它的依赖项,那么就可以将其排除:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-embedder</artifactId>
      <version>2.0</version>
      <exclusions>
        <exclusion>
          <groupId>org.apache.maven</groupId>
          <artifactId>maven-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    ...
  </dependencies>
  ...
</project>

      有时exclusions也可以用来剪掉依赖的传递关系。一个dependency可能指定了错误的作用域,或者与你项目中的其他依赖项冲突。使用通配符可以很容易地排除所有依赖项的传递关系。下面的例子表示你需要使用maven-embedder但是你希望自己管理它的依赖项,所以就排除了其全部的传递依赖:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-embedder</artifactId>
      <version>3.1.0</version>
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    ...
  </dependencies>
  ...
</project>

exclusions中包含一个或多个exclusion,每一个exclusion都包含groupId和artifactId用以表示要排除的依赖。

Inheritance -- 继承

      父项目和聚合项目需要的packaging类型是pom。packaging的类型定义了绑定到一组生命周期阶段的目标。例如,如果packaging是jar,那么打包(package)阶段将执行jar:jar目标。父POM中的大多数元素都可以被其孩子继承,包括:

  • groupId
  • version
  • description
  • url
  • inceptionYear(项目创建年份,4位数字。当产生版权信息时需要使用这个值)
  • organization(项目开发者/贡献者所属组织)
  • licenses
  • developers(项目开发者信息)
  • contributors(项目贡献者信息)
  • mailingLists(项目相关邮件列表信息)
  • scm(Source Control Management 用来配置代码库以供Maven Web站点和其他插件使用)
  • issueManagement(项目的问题管理系统)
  • ciManagement(项目持续集成信息)
  • properties(以值替代名称,Properties可以在整个POM中使用,也可以作为触发条件)
  • dependencyManagement(继承自该项目的所有子项目的默认依赖信息。这部分的依赖信息不会被立即解析,而是当子项目声明一个依赖)
  • dependencies
  • repositories(发现dependency和exclusion的远程仓库列表)
  • pluginRepositories(发现plugin的远程仓库列表,这些插件用于build和report)
  • build
    • plugin executions with matching ids
    • plugin configuration
    • etc.
  • reporting(该元素描述使用report插件产生报表的规范。当用户执行“mvn site”,这些报表就会运行。 在页面导航栏能看到所有报表的链接。)
  • profiles(在列的项目构建profile,如果被激活,会修改build)

不会被继承的元素:

  • artifactId

  • name

  • prerequisites(描述了这个项目build环境中的前提条件)

       <!-- 描述了这个项目构建环境中的前提条件。 --> 
      <prerequisites>
          <!-- 构建该项目或使用该插件所需要的Maven的最低版本 -->
          <maven></maven>
      </prerequisites>
    

Super POM

      类似于面向对象编程中对象的继承,拓展父pom的那些poms继承了来自父级的一些值。另外,正如Java中的对象最终都是继承自java.lang.Object一样,所有的项目的对象模型都是继承自一个基础的pom--Super POM。下面的片段是Maven 3.5.4的Super POM:

<project>
  <modelVersion>4.0.0</modelVersion>

  <!-- 发现dependency和exclusion的远程仓库列表 -->
  <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>
 
  <!-- 发现插件的远程仓库列表,用于build和report -->
  <pluginRepositories>
    <pluginRepository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
    </pluginRepository>
  </pluginRepositories>
 
  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <pluginManagement>
      <!-- NOTE: These plugins will be removed from future versions of the super POM -->
      <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
      <plugins>
        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
        </plugin>
        <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <version>2.2-beta-5</version>
        </plugin>
        <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>2.8</version>
        </plugin>
        <plugin>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.5.3</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
 
  <reporting>
    <outputDirectory>${project.build.directory}/site</outputDirectory>
  </reporting>
 
  <profiles>
    <!-- NOTE: The release profile will be removed from future versions of the super POM -->
    <profile>
      <!-- 构建配置的唯一标识符。即用于命令行激活,也用于在继承时合并具有相同标识符的profile -->
      <id>release-profile</id>
 
      <activation>
        <property>
          <name>performRelease</name>
          <value>true</value>
        </property>
      </activation>
 
      <build>
        <plugins>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-sources</id>
                <goals>
                  <goal>jar-no-fork</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-javadoc-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-javadocs</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-deploy-plugin</artifactId>
            <configuration>
              <updateReleaseInfo>true</updateReleaseInfo>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
 
</project>

Dependency Management -- 依赖管理

除了继承某些顶级元素外,父元素还可以为子POM们传递依赖项配置值,其中一个父元素就是dependencyManagement。

  • dependencyManagement被用于帮助POM管理所有孩子节点的依赖信息。
    例子:假设父工程my-project使用dependencyManagement定义了一个依赖"junit:junit:4.12",然后继承了该工程的一个pom只设置了 groupId=junit 和 artifactId=junit 那么Maven会通过其父pom的version将其补全。
  • 这种方式的优点:你可以在一个核心位置设置依赖信息,然后将其传播给继承了它的pom。
  • 注意:从传递依赖合并而来的artifact的version和scope也由依赖项管理部分中的版本规范控制,这可能会导致意想不到的后果。例子:你的项目有两个依赖项 dep1 和 dep2,同时dep2也依赖dep1,并且需要一个特定的版本来运行。如果此时你使用dependencyManagement来指定一个较旧的版本,那么dep2将被迫使用旧版本运行,就会失败。因此你必须检查整个依赖树(mvn dependency:tree)以避免此类问题。

Aggregation(Multi-Module)-- 聚合

      一个具有modules的项目被称为多模块或聚合项目。modules是POM列出的项目,并作为一个组执行。packaging为pom的项目可以聚合构建modules中列出的项目,这些模块是这些项目的目录或pom文件的相对路径。

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>my-parent</artifactId>
  <version>2.0</version>
  <packaging>pom</packaging>
 
  <modules>
    <module>my-project</module>
    <module>another-project</module>
    <module>third-project/pom-example.xml</module>
  </modules>
</project>

      在modules列表中不必考虑模块间的依赖关系,modules中的列表顺序也是不重要的。Maven会对这些模块进行拓展排序,这样dependency总是在依赖它的modules之前构建。

Inheritance(继承) VS Aggregation(聚合)
      聚合和继承都可以通过一个单一的高层次的POM来动态的控制build。你经常可以看到一个项目既有parents又有继承。例如,整个Maven核心通过一个基础POM org.apache.maven:maven 运行,所以在build这个Maven工程的时候可以执行一个简单的命令:mvn compile。但是聚合项目和父项目都是POM工程,他们不是一个也不一样,千万不要混淆。一个POM可以被继承但不一定具有任何聚合的modules。相反的,一个POM工程也可以聚合不从它继承的项目。

Properties

      properties是了解POM基础知识的最后一个必选项。Maven properties和Ant中的properties类似,是占位符的值。它们的值可以通过使用${X}符号在POM中的任何地方访问,其中X是属性。或者它们可以作为默认值被插件使用,例如:

<project>
  ...
  <properties>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>
  ...
</project>

它们有五种不同的风格:

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