maven 新兵营

本文介绍 maven 从安装到上手一套操作,对新手友好,跟着练一遍可以快速入门 maven。dk 的学习素材是 《maven 实战》,想要深入学习的可以看这本书。

一、安装

1. 下载 maven

maven 官方下载页面, window 电脑下载第二个文件,mac 电脑下载第一个文件,下载完解压缩即可,无需安装。接下来就是配置环境变量,这样在终端中才可以直接运行 mvn 命令。

maven下载

2. mac 下配置 maven 环境变量

编辑当前用户目录下的 .bash_profile 文件,在末尾添加如下内容。其中:MAVEN_HOME 的值替换成您电脑中 maven 文件所在路径。

MAVEN_HOME=/Users/dkvirus/Documents/apache-maven-3.5.4
PATH=$MAVEN_HOME/bin:$PATH
export MAVEN_HOME
export PATH

运行 $ source .bash_profile 让配置文件即时生效,在终端中输入 mvn -v 如果打印出版本号说明配置成功。

3. window 下配置 maven 环境变量

window 下配置 maven 环境变量与配置 jdk 变量类似,dk 由于了刚换了机器没法截图,有不会的直接谷歌/百度一下吧。

二、概述

1. maven 能做什么

maven 简化了从 项目创建 > 开发 > 测试 > 打包 整个流程,所有原本复杂/麻烦的操作现在只要在终端中敲几个命令即可完成。

maven 以优雅的方式处理 jar 包之间的依赖关系,这些关系都被定义在 pom.xml 中。

2. pom.xml 文件

每个 maven 项目根目录下都会有一个 pom.xml 文件,该文件定义了项目的基本信息,包依赖关系,插件使用等情况,类似于 npm 里 package.json 的功能,pom.xml 的基本结构如下:

<?xml version="1.0" encoding="UTF-8"?>
<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>com.dkvirus</groupId>
  <artifactId>hello-world</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>maven入门项目</name>
</project>

第一行 xml 头,指定该 xml 文档的版本为 1.0,编码方式为 UTF-8。

第二行 project 元素,是 pom.xml 的根元素。project 元素中有一些属性 xmlnsxmlns:xsixsi:schemaLocation,这些属性叫做 命名空间xsd 属性,用处是在开发工具中按【Ctrl + /】键可以提示 pom.xml 中的常用元素,而不用一个个元素的去敲。

第三行 modelVersion 元素定义当前 POM 模型的版本,maven2和maven3版本这里都填写4.0.0;

接下来介绍的三个元素 groupId、artifactId 和 version 确定一个 jar 包在 maven 仓库中的唯一性,就像点在空间中有x、y、z三个坐标确定位置一样。

第四行 groupId 元素定义项目属于哪个组,通常与项目所在的组织或公司关联。如果是个人玩的项目,比如 dkvirus,组名就叫 com.dkvirus 吧;Npm 仓库里确定一个包唯一坐标是通过 包名+版本号,很显然这种设计并不合理,像 Angular、Babel 这些大工程是不可能把所有代码都写到一个包里的,因此经常会看到 @angular/animation 这种写法,前面 @angular 标识这个包所属组织。在这一点上,maven 就显得预卜先知,在设计初就添加了 groupId 定义组的概念。

第五行 artifactId 元素定义当前 maven 项目在组中唯一的ID,项目名称;

第六行 version 元素指定当前 maven 项目的版本。SNAPSHOP 是快照的意思(通常表示还在开发中),每次发布开发版本时会自动加上时间戳, 无需手动递增版本。如果测试通过,发布正式版本,将 SNAPSHOP 去掉,如 1.0.0,没有看到 SNAPSHOP 的包通常为正式发布的稳定包。

第七行 name 元素声明一个对用户更加友好的项目名称,可以填写中文。

3. maven 目录结构

maven 项目有统一的目录结构:

|-- my-app
  |-- src
    |-- main
      |-- java                <= 主代码
      |-- resources           <= 主代码需要用到的资源,如配置文件等
    |-- test
      |-- java                <= 测试代码
      |-- resources           <= 测试代码需要用到的资源
  |-- target             
    |-- classes               <= 打包之后存放路径,.class 文件在这里哦
    |-- maven-status          <= 作用未知
  |-- pom.xml                 <= 项目对象模型

根目录下还有两个隐藏文件,

  • .classpath 定义路径用的,定义打包哪个目录下的文件,打包结果放到哪个目录下;
  • .project 作用未知。每个 maven 项目都有这个文件。

4. maven 本地仓库

所有通过 maven 下载的 jar 包都会放在 maven 本地仓库,默认位置是当前登录用户主目录下的 .m2 目录下。

代码中经常会看到 import org.junit.Test;,这是导入其它 jar 包,这个 jar 可以从 .m2 目录下找到。

三、创建项目

1. 创建目录结构

首先按照 2.3 中介绍的 maven 默认的目录结构手动创建项目 hello-world。建议别一开始就用 sts 或者 eclipse 这种 java 开发工具,找一个文本编辑器开始maven 练习吧。

maven 默认目录结构

如果每次创建新项目都要一个个手动创建目录,简直麻烦死了。maven 提供命令 $ mvn archetype:generate 可以直接生成上述目录结构,下面创建第二个 maven 项目(第6.2.2小节详细介绍,迫不及待的可以先跳转查看)做测试时会用到该命令。

2. 定义 pom.xml

看到这个目录下有 pom.xml 文件就知道这是一个 maven 项目,复制下面内容粘贴到 pom.xml 文件中。

项目组织为 com.dkvirus,项目名称为 hello-world,项目版本为 0.0.1-SNAPSHOT,项目简介为 maven 入门项目。

<?xml version="1.0" encoding="UTF-8"?>
<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>com.dkvirus</groupId>
  <artifactId>hello-world</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>maven入门项目</name>
</project>

四、开发

1. 新建文件

在 /src/main/java 目录下新建子目录 com/dkvirus/helloworld,在该目录下创建文件 HelloWorld.java(该文件完整路径为:/src/main/java/com/dkvirus/helloworld/HelloWorld.java)。

package com.dkvirus.helloworld;

public class HelloWorld {
    public String sayHello () {
        return "Hello World";
    }

    public static void main (String[] args) {
        System.out.print(new HelloWorld().sayHello());
    }
}

dk 一开始学习到这里会很疑惑为什么要在 src/main/java 目录下创建多层嵌套的子目录 com/dkvirus/helloworld,直接创建一个 helloworld 目录不是更加简洁吗??事实证明 dk 还是 too young too simple,接着往下看会找到答案。。。(第6.2.4详细介绍)

2. 编译运行

代码写好了,接下来就编译运行了。传统做法是在终端运行 $ javac HelloWorld.java 进行编译,使用 maven 只要执行 $ mvn compile 即可,该命令会自动去 src/main/java 目录下找所有 .java 结尾的文件进行编译,从这里也可以看到 maven 规定默认目录结构是有好处的。

1  dkvirus@localhost:hello-world$  mvn compile
2  [INFO] Scanning for projects...
3  [INFO]
4  [INFO] ----------------------< com.dkvirus:hello-world >-----------------------
5  [INFO] Building maven入门项目 0.0.1-SNAPSHOT
6  [INFO] --------------------------------[ jar ]---------------------------------
7  [INFO]
8  [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
9  [INFO] Copying 0 resource
10 [INFO]
11 [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
12 [INFO] Changes detected - recompiling the module!
13 [INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
14 [INFO] ------------------------------------------------------------------------
15 [INFO] BUILD SUCCESS
16 [INFO] ------------------------------------------------------------------------
17 [INFO] Total time: 2.462 s
18 [INFO] Finished at: 2018-08-04T21:27:59+08:00
19 [INFO] ------------------------------------------------------------------------

编译完成后可以去 /target/classes/ 目录下可以找到 HelloWorld.class 文件。

五、测试

1. 依赖 junit 测试包

测试 Java 代码使用 junit 测试包。传统做法是去网上找 junit 包手动下载到本地使用,使用 maven 只需将 junit 的三个坐标添加到 pom.xml 即可,maven 会自动从 maven 的中央仓库下载这个 jar 包。

<project>
  <!-- ..... --> 

  <!-- 依赖配置 -->
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>

dependencies 元素定义当前项目依赖的所有 jar 包,其子元素 dependency 定义依赖的具体包,由 groupId、artifactId、version 唯一确定包的坐标。只需添加上述配置,保存代码,maven 会自动下载 junit 包到本地以供使用。

Q1:maven 是从哪里下的呢??

A1:答案是:maven 中央仓库,全世界 java 开发者都可以往这上面提交自己的 jar 包,供他人下载使用,减少重复造轮子。

Q2:怎么知道 junit 包的三个坐标呢??

A2:进入 maven 中央仓库 这个网站,具体操作参考如下步骤:

1)在搜索框搜索包名,会列举出相关包,根据组织名(groupId)和包名(artifactId)确定目标包;

找maven包图1

2)选择指定版本,dk 通常是通过右边红框中数量多少选择具体版本;

找maven包图2

3)下方的文本域就是要找的信息,复制到 pom.xml 文件中的 dependencies 子元素位置即可。

找maven包图3

2. 编写测试代码

在 src/test/java/com/dkvirus/helloworld 目录下新建测试文件 HelloWorldTest.java 文件。

package com.dkvirus.helloworld;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class HelloWorldTest {

    @Test
    public void testSayHello () {
        HelloWorld helloWorld = new HelloWorld();

        String result = helloWorld.sayHello();

        assertEquals("Hello World", result);
    }

}

3. 编译运行

$ mvn test 会对 src/test/java 下 *Test.java 或者 Test*.java 的文件进行编译,编译后的代码放在 target 目录下。并自动执行测试代码,下方会统计测试结果,这里可以看到测试结果是通过的。尝试将测试代码改为 assertEquals("Hello World2", result);,再次执行 $ mvn test 命令查看结果。

dkvirus@localhost:hello-world$  mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.dkvirus:hello-world >-----------------------
[INFO] Building maven入门项目 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /Users/dkvirus/work/self/workspace/hello-world/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.dkvirus.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.121 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.746 s
[INFO] Finished at: 2018-08-04T21:48:51+08:00
[INFO] ------------------------------------------------------------------------

编译后的测试代码可以在 /target/test-classes/ 目录下查看到。

六、打包

1. 当前项目打成 jar 包

$ mvn package 将当前 java 项目打成 jar 包。maven 在打包前会先执行编译、测试操作;打包过程是将项目主代码打包成一个名为 hello-world-0.0.1-SNAPSHOT.jar 的文件,该文件也位于 target 目录下。打包后的 jar 包命名规则由 pom.xml 中 artifact-version.jar 的格式确定的。

dkvirus@localhost:hello-world$  mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.dkvirus:hello-world >-----------------------
[INFO] Building maven入门项目 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /Users/dkvirus/work/self/workspace/hello-world/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.dkvirus.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.1 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /Users/dkvirus/work/self/workspace/hello-world/target/hello-world-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.213 s
[INFO] Finished at: 2018-08-04T21:59:01+08:00
[INFO] ------------------------------------------------------------------------

2. 让其它项目使用该 jar 包

1)将当前 jar 包添加到 maven 本地仓库

$ mvn install 将 hello-world 输出的 jar 包安装到 maven 本地仓库中,这样其它 maven 项目通过 import com.dkvirus.helloworld.* 就可以使用 hello-world 项目中的类了。

下面再次新建一个 maven 项目 hello-test,这次不用一个个目录手动创建,直接使用 maven 命令 $ mvn archetype:generate 自动创建目录结构 。

2)maven 自动生成目录结构

|-- work
  |-- hello-world
  |-- hello-test

切换到 hello-world 所在的父目录位置:work 目录,执行 $ mvn archetype:generate 会在该目录下创建 maven 项目,这样 hello-test 和 hello-world 就是同级关系。

接下来终端会 bulabula 输出一大串东东,过一会看见光标不动了就进入交互模式,maven 会问一些问题需要你作答,这里以 hello-test 项目创建过程的交互为例进行说明。

# ------- 让你选哪个maven原型,回车选择默认即可 --------
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1219:
# ------- 让你选择maven原型的版本,回车选择即可 --------
Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
7: 1.3
Choose a number: 7:
Define value for property 'groupId': com.dkvirus        <===== 输入 groupId,我这里输入 com.dkvirus
Define value for property 'artifactId': hello-test      <===== 输入 artifactId,我这里输入 hello-test 
Define value for property 'version' 1.0-SNAPSHOT: :     <===== 输入包版本号,这里回车选择默认的 1.0-SNAPSHOT 
Define value for property 'package' com.dkvirus: : com.dkvirus.hellotest    <==== 工程的包名,这里填写 com.dkvirus.hellotest
Confirm properties configuration:
groupId: com.dkvirus
artifactId: hello-test
version: 1.0-SNAPSHOT
package: com.dkvirus.hellotest
 Y: :

第一行让选择 maven 原型项目。不同原型项目目录结构略有不同,如 maven-web 原型项目拉下来就直接是个 web 工程的目录结构,而 maven-java 项目拉下来只是个 java 工程的目录结构。当然你也可以定义自己的目录结构,上传 maven 官方仓库,再次敲这个命令时也许会看到你自己原型项目的编号。

第二行让选择 maven 原型项目的版本。maven 项目可能会更新,自然就有版本的概念了,练习的话直接回车选择默认版本即可。

3)添加 helloworld 依赖配置

修改 pom.xml 文件,在 dependencies 元素下添加 helloworld 依赖配置。

<project>

  <dependencies>
    ...
    <dependency>
      <groupId>com.dkvirus</groupId>
      <artifactId>hello-world</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
  </dependencies>

</project>

4)测试

修改 src/test/java 下的 APP.java 文件,替换为以下内容。这里可以看到引入了 helloworld 项目中包 com.dkvirus.helloworld 中的 HelloWorld.java 文件了。

回过头看一下第 4.1 留下的问题:为什么创建多层嵌套目录 com/dkvirus/helloworld 而不是直接创建子目录 helloworld 呢??如果直接创建一个子目录 helloworld,那么这里导入包语句变为 import helloworld.HelloWorld;,想一想这样不是很容易导致重名吗?因此包的命名尽量以 groupId 为前缀创建多层嵌套目录,组织名不至于那么容易重名。

package com.dkvirus.hellotest;

import static org.junit.Assert.*;
import org.junit.Test;
import com.dkvirus.helloworld.HelloWorld;

/**
 * Unit test for simple App.
 */
public class AppTest 
{
    @Test
    public void testSayHello () {
        HelloWorld helloWorld = new HelloWorld();

        String result = helloWorld.sayHello();

        assertEquals("Hello World", result);
    }
}

运行 $ mvn test 查看终端输出信息,测试通过。

dk 一开始做到这一步以为 hello-test 引用的是 maven 本地仓库的 hello-world 项目里的包,后来发现并不是。

因为 hello-world 和 hello-test 都在 work 目录下,属于同级关系,因此 hello-test 会优先引用同级目录下的 hello-world 里的 HelloWorld.java 文件,而不是去 maven 本地仓库找 jar 包。修改 work/hello-world 里的 HelloWorld.java 文件,将打印信息改成了 Hello World2,此时运行 hello-test 项目的单元测试代码,发现失败了,这一点即可证明优先级为同级目录下的 hello-world 项目。

package com.dkvirus.helloworld;

public class HelloWorld {
    public String sayHello () {
        return "Hello World2";
    }

    public static void main (String[] args) {
        System.out.print(new HelloWorld().sayHello());
    }
}

将 work/ 目录下的 hello-world 工程删除或者移动一个位置,再次运行 hello-test 项目的单元测试代码发现这一次是通过的,说明这一次引用的是 maven 本地仓库里的 hello-world 里的文件。

七、总结

到这里应该对 maven 有了大概的认识了吧,即便作为前端的 dk 现在看公司的 java 工程也比之前清晰很多,总结一下上面的内容:

  • 创建项目:$ mvn archetype:generate
  • 编译代码:$ mvn compile
  • 测试代码:$ mvn test
  • 打包代码:$ mvn package
  • 将当前包安装到本地 maven 仓库:$ mvn install

还有一个常用的命令 $ mvn clean 会删除 target 目录。通常上面几个命令执行前都会先执行 clean 命令,如:$ mvn clean install 就会先执行 clean 再执行 install,而不用分两次命令 $ mvn clean$ mvn install 执行,实在是很方便。

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

推荐阅读更多精彩内容