使用IDEA中的Maven打包是一个很频繁的操作,在多模块下Maven打包流程就会变得有些复杂,我们就来浅分析一下其中涉及的机制。

多模块打包方式有三种:

  1. 一键打包(多模块批量打包)
  2. 逐步打包(单模块多次打包)
  3. 混合打包(部分多次,部分批量)

当然我们推荐一键打包,毕竟更加智能快捷~,有利于CI/CD一键部署。

结论先行:一键打包先clean再package

1.准备模块

读者无需准备啦,有个项目框架往下看就行啦~本文根据项目框架来进行讲解

首先我们准备模块设计如下:

image-20240319201615496

具体工程目录如下:

image-20240319174713437

后续我们采用打包方式为:直接通过根目录一键打包所有模块,采用的插件是spring-boot-maven-plugin来打包各个子boot项目。且直接在idea中生成jar包,不将任何子模块install到本地Maven仓库中,以避免本地mven仓库冗余我们自己编写的代码,直接存在idea的target目录就足够了。

2.编写pom文件内容

在父模块wzyapi引入modules标签。以控制maven需要编译打包的子模块。没有引入modules的模块不会被编译打包。如果你部分模块想直接使用本地仓库的,而不使用idea中编写的就不要引入到modules中。

例如本地仓库有wzyapi-common完整模块代码了,即使idea中改动了我也不想用,我就要用本地代码,那么modules标签中一定不要包含wzyapi-common模块,让maven自己在本地寻找依赖,否则你打包出来的永远是idea中代码编译出来的依赖。

1
2
3
4
5
6
7
8
9
10
11
12
<modules>
<module>wzyapi-dependencies</module>
<module>wzyapi-common</module>
<module>wzyapi-client-sdk</module>

<module>wzyapi-framework</module>
<module>wzyapi-backend-api</module>

<module>wzyapi-interface</module>
<module>wzyapi-gateway</module>
<module>wzyapi-backend</module>
</modules>

以上我们实现了一个小功能,控制maven需要囊括编译的模块,和控制不需要囊括编译的模块(即不在modules中声明),或者说控制maven使用本地仓库代码的模块。

当然我们可以写一个模块install一个模块,来多次编译获取最新的jar包,这样就与单模块编写代码无异,这样虽然同步性强但人工操作就急剧增多,不智能^^。这种打包方式我称为逐步打包/单模块打包。

既然逐步部署同步性强,那么一键部署就会同步性较差,如果不注意就会导致代码未同步到jar包中。

先行结论,如果要保证一键部署的强同步性,在每次打包时建议先clean再打包。

2.1父模块pom

父模块完整pom.xml文件内容如下:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?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.wzy</groupId>
<artifactId>wzyapi</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>

<name>wzyapi</name>
<description>一个面向开发者的api开放平台</description>

<properties>
<!--子模块版本管理-->
<reversion>0.0.1-SNAPSHOT</reversion>

<!--plugin版本管理-->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<spring-boot-maven-plugin.version>2.7.6</spring-boot-maven-plugin.version>
</properties>

<modules>
<module>wzyapi-dependencies</module>
<module>wzyapi-common</module>
<module>wzyapi-client-sdk</module>

<module>wzyapi-framework</module>
<module>wzyapi-backend-api</module>

<module>wzyapi-interface</module>
<module>wzyapi-gateway</module>
<module>wzyapi-backend</module>
</modules>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.wzy</groupId>
<artifactId>wzyapi-dependencies</artifactId>
<version>0.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-maven-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>

</project>

2.2子模块pom

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.wzy</groupId>
<artifactId>wzyapi</artifactId>
<version>0.0.1</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<artifactId>wzyapi-backend</artifactId>
<version>${reversion}</version>
<packaging>jar</packaging>

<name>wzyapi_backend</name>
<description>wzyapi开放平台的【api后台管理系统】</description>

<dependencies>
。。。省略了依赖。。。
</dependencies>

<build>
<plugins>
<!--war包打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!--jar包打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.wzy.project.MyApplication</mainClass>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<!--git属性文件生成插件-->
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<!--开启过滤,用来指定的参数替换directory下的文件中的参数-->
<filtering>true</filtering>
</resource>
</resources>
</build>

</project>

2.3总结

1.父模块在 dependencyManagement 标签引入版本管理依赖 wzyapi-dependencies,这样就可以在父模块管理许多依赖的版本了。

但需要注意的是,在父模块引入 wzyapi-dependencies ,它内部的properties标签并不能传递,我们需要使用父模块来传递properties标签。即wzyapi-dependencies管理 dependencies中的依赖版本,父模块管理一些额外需要使用的properties标签。

2.子模块编写 dependencies 标签且无需版本号,定义子模块需要使用的依赖,一旦引入新的依赖就加到 wzyapi-dependencies 进行管理。

3.子模块编写 build 标签,定义打包需要的插件。对于boot模块即运行模块使用spring-boot-maven-plugin插件,对于jar模块和pom模块即无需启动的模块无需定义插件。

至此我们就可以实现一键打包了^^。

父模块依赖版本管理、插件版本管理;子模块定义依赖、定义插件。

父模块pom,子模块jar。

额外一个dependencies专门依赖版本管理模块为pom,并添加到父模块的dependencyManagement标签中。

3.一些问题

1.如果我们本地有某个模块的jar包,然后idea中也有某个模块的jar包,会优先使用哪个?

答案是:如果modules管理了这个模块,就优先idea中的jar包;如果modules没有管理这个模块就使用本地jar包。

2.如果我们更新B模块代码然后立即一键打包,那么依赖B模块的A模块jar包中能得到最新的B模块代码吗?

答案是:不能。至于为什么我还没有找到说法,估计是maven为了优化打包速度导致的,没有代码改变的模块明显打包速度更快,导致打包的代码是上次缓存的代码,即同步性差。这样的话我们clean一下同步性就好了,咱就是手动删除缓存呗。

我们进一步说明一下这个问题:

第一次打包

这是第一次打包项目时的耗时输出:

image-20240319193908727

第一次backend模块打包:9 S左右,common模块2秒左右

我们再查看一下backend模块依赖的common模块的jar包,内容如下:

image-20240319194034527

我们在IDEA更改一下common模块代码,然后立即再次打包package,且不进行clean(注意package不会包含clean操作的!!!网上有人误认子弟啊)。

image-20240319194432201

第二次打包

这次我们只更改了common模块代码,其它模块会感应到变化更新打包内容吗?不会**

这是第二次打包项目时的耗时输出:

image-20240319194636006

第二次backend模块打包:1 S左右,common模块2秒左右

从时间上看也就知道了common模块代码变化了,打包时感应到了就完全重新打包;其它模块代码无变化,打包时就感应不到别的模块代码变化,就会缓存打包。即模块变化是隔离的,maven模块打包缓存清空是隔离的。

我们再查看一下backend模块依赖的common模块的jar包,内容如下:

image-20240319195133738

(⊙﹏⊙)代码没有变化。。。其它模块查看一下common的jar包同样如上图。

那么我们再查看一下common模块自己生成的jar包,内容如下:

image-20240319195456880

哇~找到了我们更新的代码

所以,听话先clean再package,避免缓存生效,确保java代码和jar包代码的同步性。

或者我们不用idea的按钮了,直接控制台 mvn clean install package ‘-Dmaven.test.skip=true’。当然这样我们需要手动输入还要记忆命令**

4.总结

  1. 父模块依赖版本管理、插件版本管理、子模块版本管理子模块定义依赖、定义插件。父模块pom,子模块jar。额外一个dependencies专门依赖版本管理模块为pom,并添加到父模块的dependencyManagement标签中。
  2. 先clean再package