# Maven 进阶
# 参考文档
# 配置文件
# 继承与聚合
虽然在开发的时候,会将继承和聚合一起使用,但是这是两个完全不同的概念。
# 继承
继承就是子模块自动继承父模块的一些依赖、插件、属性等。
继承的目的是为了消除重复性,实际项目中最常用的就是将子模块 POM 中很多相同的配置提取出来统一锁定在父模块 POM 中。如 groupId、artifactId、version 等。然后在使用的时候,子模块会继承到父模块的版本号,子模块不需要再指定版本号,方便统一管理项目依赖。
可以继承到的资源有:

继承使用的元素是 <parent>,子模块必须指定父模块。父模块坐标中的 <packaging> 必须是 pom,否则子模块不会继承。
父模块中实现依赖管理的元素是 <dependencyManagement>:
<dependencyManagement>只是声明依赖,不会引入,只有子模块中的<dependency>中指定后,才会解析依赖。
注意:<dependencyManagement> 中的版本号会覆盖所有的传递依赖的版本号!!
# 聚合
聚合就是将多个模块统一进行打包,他们是整体与部分的关系。聚合就是在执行 maven 命令构建项目时,整个项目会被打包在一起。
聚合使用的元素是 <module>,注意 <module> 中的是相对路径,被聚合的顺序也不是声明的顺序(实际是按照依赖关系来执行的)。
简单讲就是执行总模块的 compile,那就会执行所有被聚合的 compile。
# 跳过测试
命令后添加参数 -D skipTests,例如 mvn install -D skipTests。
# 打包 package
# Spring boot 打包插件
以常用的 spring boot 举例,spring boot 工程创建好之后,会发现 pom.xml 最下面默认配置了一个打包插件,如果是手动创建的 maven 项目就要手动添加这个插件了。
<build>
<plugins>
<plugin>
<!-- 该插件会将 jar 运行所有需要的依赖 jar 打包进去 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2
3
4
5
6
7
8
9
通过 IDEA 可以看到 maven 中包含了 spring-boot-maven-plugin 插件:

Goal 功能说明:
build-info:生成项目的构建信息文件build-info.properties。repackage:这个是 默认 goal,在mvn package执行之后,这个命令再次打包生成可执行的 jar,同时将mvn package生成的 jar 重命名为*.origin。run:这个可以用来运行 Spring Boot 应用。start:这个在mvn integration-test阶段,进行 Spring Boot 应用生命周期的管理。stop:这个在mvn integration-test阶段,进行 Spring Boot 应用生命周期的管理。
spring-boot-maven-plugin 插件默认在父工程 sprint-boot-starter-parent 中被指定为 repackage,如果需要设置其他属性,需要在当前应用的pom中进行设置。可以点击sprint-boot-starter-parent 进入父 pom 进行查看,如下图:

# 打包后结构
配置了 spring-boot-maven-plugin 插件后,执行 mvn clean install 命令后,会在 target 目录下生成打包后的信息:

打包后会有两个 jar 信息,一个是 mvn install 原始打包生成的 jar 包,一个是 spring-boot-maven-plugin repackage 之后生成的可执行 jar 包。
# 可执行 jar
可执行 jar 的内部结构:

其中
BOOT-INF主要是一些启动信息,包含 classes 和 lib 文件,classes 文件放的是项目里生成的字节文件 class 和配置文件,lib 文件是项目所需要的 jar 依赖。META-INF目录下主要是 maven 的一些元数据信息,MANIFEST.MF文件内容如下:Manifest-Version: 1.0 Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx Implementation-Title: play Implementation-Version: 1.0-SNAPSHOT Spring-Boot-Layers-Index: BOOT-INF/layers.idx // Start-Class 是项目的主程序入口,即 main 方法。 Start-Class: cn.test.controller.PlayApp // Springboot-Boot-Classes 和 Spring-Boot-Lib 指向的是生成的 BOOT-INF 下的对应位置。 Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.4.5 Created-By: Maven Jar Plugin 3.2.0 Main-Class: org.springframework.boot.loader.JarLauncher1
2
3
4
5
6
7
8
9
10
11
12
13
14其中
Main-Class属性值为org.springframework.boot.loader.JarLauncher,这个值可以通过设置属性layout来控制:<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--使用 -Dloader.path 需要在打包的时候增加 <layout>ZIP</layout>,不指定的话 -Dloader.path 不生效--> <layout>ZIP</layout> <!-- 指定该jar包启动时的主类[建议],即启动类 --> <mainClass>cn.test.controller.PlayApp</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
打包出来的可执行 jar 之所以可以使用 java -jar 运行,和 MANIFEST.MF 文件里的配置关系密切。
# Original jar
原始 jar 的内部结构:

可以看到通过 mvn package 构建的 jar 是一个普通的 jar,包含的都是项目的字节文件和一些配置文件,没有将项目依赖的第三方 jar 包含进来。再看下 MANIFEST.MF 文件:
Manifest-Version: 1.0
Implementation-Title: play
Implementation-Version: 1.0-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Jar Plugin 3.2.0
2
3
4
5
其中没有包含 Start-Class、Main-Class 等信息,这个与可执行 jar 的该文件存在很多差异,而且目录结构也有很大差异。
# 可执行 jar 的一些问题
一般对使用 spring-boot-maven-plugin 插件打出的可执行 jar 不建议作为 jar 给其他服务引用。
因为可能出现访问可执行 jar 中的一些配置文件找不到的问题。如果想让构建出来的原始 jar 不被重新打包,可以对 spring-boot-maven-plugin 插件配置 classifier 属性,自定义一个可运行 jar 名称,这样该插件就不会对原始的 jar 重命名操作了。
<configuration>
<!-- 指定该 jar 包启动时的主类[建议],即启动类 -->
<mainClass>cn.test.controller.PlayApp</mainClass>
<classifier>repackageCanExec</classifier>
</configuration>
2
3
4
5
- 配置的
classifier表示可执行 jar 的名字,配置了这个之后,在插件执行repackage命令时,就不会给mvn package所打成的 jar 重命名了,这样就可以被其他项目引用了,classifier命名的为可执行 jar。

这样原始 jar 就可以正常被引用当前包下的内容了。
# 私服
# 讨论区
由于评论过多会影响页面最下方的导航,故将评论区做默认折叠处理。