Maven----让我们更轻松得管理我们的依赖

1.Maven介绍

Maven是一个基于项目对象模型(POM)的概念的纯java开发的开源的项目管理工具。主要用来管理java项目,进行依赖管理(jar包管理,能自动分析项目所需的依赖软件包,并到Maven仓库区下载)和项目构建(项目编译、打包、测试、部署)。此外还能分块开发,提高开发效率。

2.Maven下载

http://maven.apache.org/download.cgi

3. Maven安装

3.1 解压

注意: 解压文件尽量不要放在含有中文或者特殊字符的目录下。
解压后,有如下目录:
bin:含有mvn运行的脚本
boot:含有plexus-classworlds类加载器框架,Maven 使用该框架加载自己的类库。
conf:含有settings.xml配置文件
lib:含有Maven运行时所需要的java类库

3.2 环境变量

maven依赖java环境,所以要确保java环境已配置好 (maven-3.3+ 需要jdk7+)
maven本身有2个环境变量要配置:
MAVEN_HOME = maven的安装目录
PATH = maven的安装目录下的bin目录

3.3 测试

查看maven版本信息:mvn -v

4.Maven的配置

maven的conf目录下,有maven的配置文件 settings.xml
添加 <localRepository> 配置maven的 "本地仓库" 位置,用于存放项目中的相关依赖(比如 所有jar)

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0                http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  -->
  <localRepository>D:\Program Files\maven\myrepository</localRepository>

<profiles> 标签中 增加 一个 <profile> 标签,限定maven项目默认的jdk版本.
内容如下:

<profiles>
    <!-- 在已有的profiles标签中添加profile标签 -->
    <profile>    
        <id>myjdk</id>    
        <activation>    
            <activeByDefault>true</activeByDefault>    
            <jdk>1.8</jdk>    
        </activation>    
        <properties>    
            <maven.compiler.source>1.8</maven.compiler.source>    
            <maven.compiler.target>1.8</maven.compiler.target>
            <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> 
        </properties>    
    </profile>
</profiles>
<!-- 让增加的 profile生效 -->
<activeProfiles>
    <activeProfile>myjdk</activeProfile>
</activeProfiles>

5.仓库

讲完了maven的基本配置,我们来介绍一下仓库的概念
Maven 仓库
在 Maven 的术语中,仓库是一个位置(place)。
Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫做仓库
在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件
Maven 仓库能帮助我们存储、管理构件的地方。
操作模式:开发人员定义简单的配置,描述需要哪些依赖,maven自动从仓库中下载依赖(jar).

5.1 仓库分类

repository1.jpg

repository2.jpg

5.2本地仓库

1.在安装 Maven 后并不会创建,第一次执行 maven 命令时才创建。
2.Maven 所需要的构件都是直接从本地仓库获取的。
如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。
3.默认情况下,不管Linux还是 Windows,每个用户在自己的用户目录下都有一个路径名为
.m2/respository/的本地仓库目录。可以在setttings.xml中修改。

5.3 远程仓库

5.3.1中央仓库

Maven 中央仓库是由 Maven 社区提供的仓库,
其中包含了绝大多数流行的开源Java构件。是默认的远程仓库。(网站在国外,网速杠杠的,绝对超过5kb/s)
https://mvnrepository.com/ 可以搜索需要的依赖的相关信息(仓库搜索服务)
http://repo.maven.apache.org/maven2/ 中央仓库地址

5.3.2 远程仓库(自建的远程仓库)

如果 Maven 在中央仓库中也找不到依赖的文件,它会停止构建过程并输出错误信息到控制台。 为避免这种情况,Maven 提供了远程仓库的概念,它是开发人员自己定制仓库。 比如aliyun仓库(http://maven.aliyun.com/nexus/content/groups/public/
(我用阿里云主要是因为它快,毕竟是国内的网站,但我搜索一般上mvn官网搜索,阿里云的搜索就是渣啊~~~~)

6. idea中使用Maven

6.1 在idea中关联maven
idea_maven.jpg

6.2创建一个Maven项目


6.2.1 选择骨架

骨架中包含一些初始化的项目结构


create_prj1.jpg
6.2.2 指定项目名称
create_prj2.jpg
6.2.3 设置Maven
create_prj3.jpg
6.2.4项目位置
create_prj4.jpg
6.2.5 项目结构

1> src/main/java 存放源代码,建包,放项目中代码(service,dao,User,....)
2> src/main/resources 书写配置文件,项目中的配置文件(jdbc.properties)
3> src/test/java 书写测试代码,项目中测试案例代码
4> src/test/resources 书写测试案例相关配置文件
5> 项目根/pom.xml (project object model) maven项目核心文件,其中定义项目构建方式(war包,jar包,默认为jar包,web项目用war包),声明依赖等
注意:项目中的建包,建类,执行,都和普通项目无差异

create_prj5.jpg

6.3 导入依赖jar

建好项目后,需要导入需要的jar,要通过【坐标】(GAV)
1. 每个构件都有自己的坐标(标识) = groupId + artifactId + version = 项目标识 + 项目名 + 版本
2. 在maven项目中只需要配置坐标,maven便会自动加载对应依赖。删除坐标则会移除依赖
3. jar包并不会导入到项目目录中,只是maven帮助做了关联。但在打包部署项目时会携带对应依赖
4. 而且,如果导入了依赖A,而A中又依赖了B,则B会被自动加载。

6.3.1 依赖的查找

依赖查找服务:https://mvnrepository.com/ ,获得依赖的坐标,在maven项目中导入。(个人推荐每个人都准备个个人依赖库,打好注解方便使用)
1.mvn官网搜索

dependency1.jpg

2.进入依赖详情页
dependency2.jpg

6.3.2 导入依赖

在项目的pom文件中
dependency4.jpg
6.3.3 同步依赖

引入坐标(GAV)之后同步依赖,确认导入


auto_import.jpg
6.3.4 依赖生命周期

项目中导入的依赖可以做生命周期的管理

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
    <!-- 生命周期 -->
    <scope>compile</scope>
</dependency>

**maven有生命周期如下:
1 compile:缺省值,适用于所有阶段(测试,编译,运行,打包),会随着项目一起发布(被打包)
2 provided:类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar参与测试,编译,不会被打包
3 runtime:只在运行时使用,如 jdbc6.jar,适用运行和测试阶段,会被一起发布
4 test:只在测试时使用,用于编译和运行测试代码,如 junit.jar,不会随项目发布
5 system:类似provided,但Maven不会在Repository中查找它,要在本地磁盘目录中查找,参与编译,测试,打包,运行。

6.4 创建Web项目

6.4.1 选择web骨架
web1.jpg
6.4.2 定义项目坐标
web2.jpg
6.4.3 选择maven
web3.jpg
6.4.4 选择项目磁盘位置
web4.jpg
6.4.5 maven-web项目结构
web5.jpg
6.4.6 导入依赖

maven-web项目中没有携带servlet相关的依赖,所以需要导入。
否则,servlet相关的组件不能使用,jsp也不能使用

7. idea-tomcat

7.1 编辑运行环境
tomcat1.jpg
7.2 新增tomcat-server
tomcat2.jpg
7.3 选择tomcat位置
tomcat3.jpg
7.4 tomcat细节
tomcat4.jpg

8. 部署web项目

8.1 新增部署
deploy.jpg
8.2 增加artifacts

如下,项目部署有两种选择:war和war-exploded
war:将项目实际部署到tomcat的web-apps目录中.
war-exploded:并不会真正部署到tomcat的web-apps目录,tomcat直接访问项目内容(我们选这个)

deploy2.jpg

部署.jpg

8.3 设置访问路径
deploy3.jpg
8.4 启动tomcat
deploy4.jpg

deploy5.jpg

9.Aliyun仓库(必需)

中央仓库在国外,下载速度很慢,国内的aliyun仓库是一款很优秀的maven仓库。强烈建议使用
在setting.xml文件中配置如下信息

<mirrors>
    <mirror>
        <id>aliyun</id>  
        <!-- 中心仓库的 mirror(镜像) -->
        <mirrorOf>central</mirrorOf>    
        <name>Nexus aliyun</name>
        <!-- aliyun仓库地址 以后所有要指向中心仓库的请求,都由aliyun仓库接替-->
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>  
    </mirror>
</mirrors>

10.maven特性

10.1 传递性

10.1 传递性

所谓传递性,是指,在项目中导入了依赖A,而A内部依赖了B,则A会自动触发B也被导入项目

好处:没必要导入所有依赖在项目中,传递性会自动导入一些依赖。简化依赖导入管理
坏处:jar冲突

10.1.1 jar冲突
<!-- 冲突示例 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.8.3</version>
</dependency>
jar_conflict.jpg

jar冲突:A依赖B B依赖C和d-1.0 c依赖d-2.0。则此时导入A依赖,那么项目中会采用d-1.0 还是 d-2.0?
maven默认的选择原则是“最短路径”
1 d-1.0的路径 A->B->d-1.0
2 d-2.0的路径 A->B->C->d-2.0 则最终选择的是d-1.0 ( 如果路径等长,则先声明的优先 )

解决方案
默认的选择原则明显是不能符合实际的需求的,所以有其他设置可以改变默认原则
1> 排除原则:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.6.RELEASE</version>
<exclusions>
   <!-- 排除一个依赖,则此依赖不会被spring-context-suport传递导入 -->
   <exclusion>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
   </exclusion>
</exclusions>
</dependency>

2> 明确定义:直接定义<dependency> ,则根据最短路径,设置明确版本
3> 在父项目中定义,则子项目中在传递依赖时会以父项目中的声明为准。

10.1.2 传递依赖范围

前置概念:
项目A中 依赖 B库 ,B库 依赖C库;
则A对B是第一直接依赖,B对C是第二直接依赖,A对C是传递性依赖
如果第一直接依赖范围是:compile,第二直接依赖范围:compile,则传递性依赖范围就是:compile
则,传递性依赖的范围取决于 第一和第二直接依赖

compile ( 测、编、运) runtime( 测、运) provide( 测,编) test( 测 )
complie compile runtime - -
runtime runtime runtime - -
provide provide provide provide -
test test test - -

如上表格:第一列是第一直接依赖范围,第一行是第二直接依赖,交叉的格子是传递性依赖范围。

解释第一行:第一compile,第二compile,则传递compile;

​ 第二runtime,则传递runtime; (B运行时需要C,所以A也要保证运行时有C)

​ 第二provide,则不加载传递依赖;( A不需要测试B,也不需要编译B,所以不需要C)

​ 第二test,则不加载穿刺依赖;(A不需要测试B,所以不需要C)

其他行,也是同样的逻辑关系。

结论:A依赖B,B依赖C ==> A中不一定会加载C;即使加载C,C的scope也会有变动。

10.2 继承
  • 多个maven项目间难免有重复的pom配置,重复的配置没必要重复写,maven提供了pom的父子集成机制,类似java的父子类。将共用的pom配置定义在父pom中,自pom继承后就等价持有了父pom中的内容。实现pom复用。
  • 项目中的诸多依赖,需要统一管理版本,不建议每个开发者随意指定版本。可以在父项目中声明版本信息,子项目延用即可
10.2.1 定义父项目

定义空白的maven项目(不用写任何代码,只用他的pom文件)

extends.jpg

注意:子项目中如果明确定义了版本,则父项目中的声明会失效。所以父项目中声明的版本,子项目中不允许再定义。

10.2.2 子项目继承

子项目pom中继承父pom


extends2.jpg
10.3 聚合

一个项目(project),可以分为多个模块(module),使得项目更加层次清晰,利于管理。利于模块复用。

模块的划分,常用的方式之一:基于MVC划分模块,进一步树立项目层次,对于大型项目尤为实用。

每个模块都是一个单独的maven项目,由一个父项目来做聚合。
模块之间可以互相依赖。
对父项目做编译,清理,打包等行为会传播给所有模块。

10.3.1 创建父模块

可以基于 “quick-start”骨架建立项目,或建立一个空白项目均可。
父模块必须是pom打包!!,其他没特殊要求。

聚合1.jpg

10.3.2 创建子模块

父模块作为:Project,再建项目作为:Module
注意:dao和service模块打包格式为jar,web模块打包格式为war


  1. 在父模块上 "右键", 为其新建 Module


    聚合2.jpg

2. Module的创建,选择骨架时,按需选择即可;如下填写Module的项目名即可

聚合3.jpg


3. 创建完毕后,如下

聚合4.jpg


4. 重复如上过程,再多创建几个Module

聚合5.jpg


5. 父项目的pom,已经发生了改变,<moludels>标签即在做多模块聚合

聚合6.jpg

10.3.3 父项目中定义版本管理
<!-- 共用的依赖定义 -->
<dependencies>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>
    ....
</dependencies>
<!-- 依赖版本统一管理 -->
<dependencyManagement>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.3.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.6.RELEASE</version>
    </dependency>
    ....
</dependencyManagement>
  • 统一管理:

    • 如上可以避免在每个子模块中都声明版本号,这样想升级或者切换到另一个版本时,只需要在父模块里更新,不需要任何一个子模块的修改;
  • 自定义:

    • 如果某个子模块需要另外一个版本号时,只需要在dependencies中声明一个版本号即可。子模块就会使用子模块声明的版本号,不继承于父模块版本号。

在做依赖管理时,也有时会在父项目中不做依赖管理,而是明确导入所有依赖,子模块直接继承后就拥有哪些依赖。service模块只需要单独导入dao,web模块只需要单独导入service。

10.3.4 子模块中定义内容

各个模块中,分别定义对应的依赖、代码、配置。
子模块中需要的配置文件,可以由子模块持有,也可以统一交给web模块持有。
来自不同模块的组件间的注入,编码不变。


dao模块,定义所有DAO。其中的pojo也可以分为一个单独的模块。

聚合9.jpg


service模块,定义所有Service类。需要导入dao模块,进而注入其中的DAO组件

聚合10.jpg


web模块,定义所有的Controller类,需要导入service模块(同上)

聚合11.jpg

10.3.5 测试

如果dao模块自己持有配置,则可以在dao模块进行测试。反之可以将dao模块导入到web模块,在web模块中测试dao。
Service测试同理。
web模块的测试,可以直接将web模块部署到tomcat即可测试。

10.3.6 部署

在父模块所在目录下,直接执行:mvn package, 即可完成所有模块的打包,且web模块的war包中会包含dao,service,pojo模块的jar。

直接部署web模块的war即可。

10.3.7 相关指令
  • 测试,对父模块做 打包操作
    mvn package
  • 测试,对父模块做 清理并编译
    mvn clean compile
  • 测试,对父模块做 清理并打包
    mvn clean package
  • 注意:对父模块做的操作都会同步到每个模块上。

11. 私服

私服是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构件。
有了私服之后,当 Maven 需要下载构件时,直接请求私服,私服上存在则下载到本地仓库;否则,私服请求外部的远程仓库,将构件下载到私服,再提供给本地仓库下载。
私服可以解决在企业做开发时每次需要的jar包都要在中心仓库下载,且每次下载完只能被自己使用,不能被其他开发人员使用
所谓私服就是一个服务器,但是不是本地层面的,是公司层面的,公司中所有的开发人员都在使用同一个私服

私服1.png

私服2.png

我们可以使用专门的 Maven 仓库管理软件来搭建私服,比如:Apache ArchivaArtifactorySonatype Nexus。这里我们使用 Sonatype Nexus

11.1 Nexus安装

  • 下载nexus-2.x-bundle.zip,解压即可
  • 解压后在bin目录中执行:nexus install 在系统中安装nexus服务
    nexus uninstall 卸载nexus服务
    nexus start 启动服务
    nexus stop 停止服务
  • 访问私服:http://localhost:8081/nexus/
  • 登录私服:admin/admin123


    私服3.jpg

11.2 Nexus登录

私服_login.jpg

私服_login2.jpg

11.3 仓库列表

group:包含多个仓库,通过group库的地址可以从包含的多个仓库中查找构件 hosted: 私服 服务器本地的仓库,其中存储诸多构件。 3rd存放中央仓库没有的 ,如ojdbc.jar,可以上传到私服的该库中。 Releases存放项目的稳定发布版本,一个模块做完后如果需要共享给他人,可以上传到私服的该库。 Snapshots对应不稳定的发布版本 proxy:代理仓库,其会关联一个远程仓库, 比如中央仓库,aliyun仓库,向该仓库查找构件时,如果没有会从其关联的仓库中下载。
私服_list.jpg

11.4 导入第三方jar到私服

有些jar在中心库中是没有的,比如oracle的数据库驱动jar:ojdb6.jar. 可以自己下载jar,然后上传到私服中。
此种构件,我们建议放在 "3rd party" 仓库中 ( 存放第三方构件 )

私服_addjar1.jpg

私服_addjar2.jpg

私服_addjar3.jpg

私服_addjar4.jpg

11.5 maven配置私服

在maven中配置私服,使得maven可以从私服上获取构件

11.5.1 仓库组

而此时就有问题,私服中有很多仓库,每个仓库都有自己的url,则项目中配置哪个仓库呢 ?
私服中有一个仓库组,组中包含多个仓库,可以指定仓库组的url,即可从多个仓库中获取构件

私服_deploy2.jpg

11.5.2 maven关联私服

配置settings.xml,设置私服地址、认证等信息
配置私服 username/password

私服_deploy1.jpg

More Actions配置私服为maven的远程仓库,注意:【此处的repository的id要和 上图中server的id保持一致,才可以在私服认证通过】
私服_deploy3.jpg

More Actions使profile配置生效
私服_deploy4.jpg

至此,maven项目中需要依赖时,maven会从私服中下载

11.6 maven项目部署到私服

在项目的pom中配置部署仓库位置:
执行 :mvn deploy 即可将项目部署到私服对应的仓库中,此时项目中的打包方式多为jar

私服_deploy5.jpg

注意:如上的 repositoryid 依然是要和settings.xml中配置的 server 中的 id 对应才能通过私服的认证

12. mave常用指令

mvn compile 编译
mvn clean 清除上次编译的内容
mvn clean compile 清除并编译
mvn package 打包

13. Resources

Resources插件,负责将配置文件复制到编译目录中。两种配置文件,main-resources、test-resources
Maven默认只关注 resources目录下的配置文件,其他目录下的配置文件会被忽略。

13.1 增加资源目录

如果有配置文件不在 resources目录下,则需要额外配置:

<build>
    <resources>
        <!-- src/main/java目录也是一个资源目录,其中的配置文件也会被复制到编译目录 -->
        <resource>
            <directory>src/main/java</directory>
        </resource>
    </resources>
</build>
13.2 排除或包含

如果有资源目录中有需要排除的配置文件,可以如下设置:

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