Maven 聚合工程的几个小细节

Maven 作为我们 Java 开发领域最最基础的工具,估计每个小伙伴每天都在用。但是对于新手而言,Maven 中有一些基本的概念似乎很容易搞混,例如继承、聚合、集成等等。所以今天我们就来简单聊聊这个话题。

为什么需要分模块

松哥在 vhr(https://github.com/lenve/vhr) 项目中也用到了聚合工程,那么为什么用聚合工程?直接一个工程分包不行吗?

如果只是一个小 case,分包当然是可以的,但是如果项目越来越大,分包的弊端就会逐步展现出来:

  • pom.xml 中引用的依赖非常多以至于难以维护。
  • 同时在修改 dao 层的代码,结果改错了,你在 service 层编译不通过,烦躁。
  • 项目越来越大,build 等待时间也越来越长。
  • 有一个新的项目想要复用你的 utils 工具包,结果你只能去拷贝代码。

这样高度耦和的代码实际上并不符合我们的设计规范,所以我们需要对代码进行拆分,做成不同的模块。

模块划分

经过模块划分后,我们的 Maven 项目结构可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└── vhr-parent
├── vhr-dao
│   ├── pom.xml
│   └── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│   └── java
├── vhr-service
│   ├── pom.xml
│   └── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│   └── java
└── vhr-web
├── pom.xml
└── src
├── main
│   ├── java
│   └── resources
└── test
└── java

先说下代码组织形式,可以是父子形式,就像上面这样,父工程是一个目录,子工程的目录在父工程目录中。也可以是平铺的形式,即父子工程在同一目录下。两种形式皆可,但是在配置上会略有差异,这个松哥后面会说,这里我们先按照上面这种代码组织形式来讲。

service 依赖 dao,web 依赖 service,默认情况下,依赖是可以传递的,所以你在 web 中也可以使用 dao。一些特殊的情况下,如果我们不希望依赖传递,则可以使用 scope 节点进行配置。

一般来说,dao、service 都是打包成 jar、web 打包成 war,parent 的 packaging 类型则是一个 pom。

按照模块划分之后,上面我们所提到的问题,现在都解决了:

  • 首先,项目变大之后,build 等待时间并不会飞速增长,各个模块都可以独自 build。
  • 拆分之后,pom.xml 文件中的依赖也不再凌乱。
  • 如果其他项目有需要,可以方便的将某一个模块提供给其他项目使用。
  • 依赖版本可以使用 depencencyManagement 节点进行统一管理。spring-boot 中的 parent 就是这么干的。

配置细节

首先我们来看下 vhr-parent 中的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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>org.javaboy</groupId>
<artifactId>vhr-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>vhr-dao</module>
<module>vhr-service</module>
<module>vhr-web</module>
</modules>
</project>

前面我们说过了,parent 的 packaging 节点要为 pom,这个是固定的,只要有子模块,packaging 就是 pom。

另外我们还在 parent 的 pom.xml 中配置了 modules,将其所包含的所有子模块都列出来,当我们在 parent 处进行打包时,Maven 会自动梳理子模块之间的依赖关系,整理出来一个 build 顺序,然后进行编译打包。

再来看看子模块的 pom.xml 配置,以 vhr-service 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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">
<parent>
<artifactId>vhr-parent</artifactId>
<groupId>org.javaboy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>vhr-service</artifactId>
<dependencies>
<dependency>
<groupId>org.javaboy</groupId>
<artifactId>vhr-dao</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

子模块中需要配置 parent,这样 vhr-service 就继承了 parent 的一切,包括依赖、插件、坐标、版本号等等,有了来自 parent 的一大堆东西之后,vhr-service 自己的 pom.xml 中只需要简单配置一下 artifactId 即可。依赖的版本号则可以通过 ${project.version} 变量引用项目的版本号。

这里没有配置 packaging,默认就是 jar,所以可以省略,如果是 web 模块,则还需要配置 packaging 为 war。

打包

聚合工程,听名字就知道是很多工程聚在一起组成一个完整的项目,所以打包的时候,也是一起打包。直接在 parent 处执行 mvn package 命令,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
sang-3:vhr-parent sang$ mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] vhr-parent [pom]
[INFO] vhr-dao [jar]
[INFO] vhr-service [jar]
[INFO] vhr-web [jar]
[INFO]
...
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for vhr-parent 1.0-SNAPSHOT:
[INFO]
[INFO] vhr-parent ......................................... SUCCESS [ 0.004 s]
[INFO] vhr-dao ............................................ SUCCESS [ 0.867 s]
[INFO] vhr-service ........................................ SUCCESS [ 0.038 s]
[INFO] vhr-web ............................................ SUCCESS [ 0.025 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.021 s
[INFO] Finished at: 2021-01-26T12:50:42+08:00
[INFO] ------------------------------------------------------------------------

可以看到整个打包过程,各个聚合工程都被打包了。打包完成后,在不同模块的目录下都可以看到一个 target 目录,里边就有刚刚打包好的 jar 或者 war。

如果你使用的是 IntelliJ IDEA,也可以在工具右侧找到 Maven->LifeCycle->Package,双击进行打包。

目录问题

在前面的案例中,我们的代码结构使用了父子目录的形式,但是在实际应用中,有的时候我们可能会采用平铺的形式,像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
└── vhr
├── parent
│   ├── pom.xml
├── vhr-dao
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   └── resources
│   │   └── test
│   │   └── java
├── vhr-service
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   └── resources
│   │   └── test
│   │   └── java
└── vhr-web
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│   └── java

可以看到,parent 和各个子模块处于同一目录下,这个时候,无论是 parent 的 pom.xml 还是子模块的 pom.xml,写法都会和之前略有差异。

先来看 parent 的 pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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>org.javaboy</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>../vhr-dao</module>
<module>../vhr-service</module>
<module>../vhr-web</module>
</modules>

</project>

由于 module 中定义的是子模块名称,所以,当子模块和父模块处于同一目录下时,需要明确指出子模块的位置,因此这里用到了相对路径。

同理,在子模块中也需要明确指定父模块的 pom.xml,以 vhr-service 为例,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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">
<parent>
<artifactId>parent</artifactId>
<groupId>org.javaboy</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>vhr-service</artifactId>
<dependencies>
<dependency>
<groupId>org.javaboy</groupId>
<artifactId>vhr-dao</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>


</project>

可以看到,在 parnet 节点中多了 relativePath 用来指定父模块的 pom.xml。

# Java, Maven

喜欢这篇文章吗?扫码关注公众号【江南一点雨】【江南一点雨】专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×