Maven高级
继承与聚合
继承
继承:即子工程可继承父工程的配置信息
- 定义一个父工程
parent - 定义创建许多子工程
item,子工程共同用到的依赖 (如lombok)可定义在父工程中实现依赖传递。
定义父工程
如果一个工程为父工程,其打包方式必须设置为pom(默认为jar)。
在pom.xml文件根标签中定义如下:
xml
<packaging>pom</packaging>
常见的打包方式
jar:普通模块打包,内嵌tomcat运行war:普通web程序打包,需要部署到外部tomcat服务器上运行pom:父工程 或聚合工程 ,该模块不写代码,仅进行依赖管理
定义子工程
在子工程的<parent>中声明父工程坐标信息
例:
xml
<parent>
<groupId>com.gezishan</groupId>
<artifactId>item-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../item-parent/pom.xml</relativePath>
</parent>
Spring工程也用到了maven继承,所有SpringBoot创建的项目都继承了spring-boot-starter-parent工程:
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这样,子工程就继承了父工程了,众多子工程公共用到的配置可以在父工程中声明(如依赖的版本)。
注:maven中一个工程只能继承一个父工程,即只能单继承 ,不能多继承。
版本管理
父工程在pom.xml的<dependencyManagement>中统一声明依赖版本
xml
<dependencyManagement> <!-- 集中声明版本 -->
<dependencies> <!-- 版本管理子项 -->
<dependency> <!-- 具体依赖坐标 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version> <!-- 统一版本 -->
</dependency>
<dependency>
...
</dependency>
</dependencies>
</dependencyManagement>
注:<dependencyManagement>只是管理依赖版本,但不实际引入,具体引入是由子类工程决定的。
子工程引入依赖
xml
<dependencies> <!-- 版本管理子项 -->
<dependency> <!-- 具体依赖坐标 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 无需指定版本 -->
</dependency>
<dependency>
...
</dependency>
</dependencies>
统一还可以通过自定义属性的方法实现,这样要写的代码少一点。
自定义属性 :在<properties>中定义(一般在父工程中定义)
xml
<properties>
<lombok.version>1.28.24</lombok.version>
</properties>
引用属性 :在需要的插槽使用${}引用(一般在子工程中引用)
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
聚合
聚合:将多个项目模块组织成一个整体(这样可以同时进行项目的构建与运行)。
如果有多个工程需要构建,一个一个工程进行构建,太麻烦了;可以利用聚合,将多个工程管理起来,实现一键构建安装。
聚合工程 :一个不具有业务代码的"空"工程,有且仅有一个pom.xml文件。
一般将父工程作为聚合工程,子工程作为被聚合的工程,将一个父工程进行安装,其他被聚合的(子)工程也同时进行安装。
实现
- 在父工程中定义
<modules>标签设置当前工程所包含的子模块
xml
<modules>
<module>../pojo</module> <!--填入模块地址-->
<module>../utils</module>
...
</modules>
胖包与痩包
Maven对项目打包有两种方式胖包和痩包,胖包 包含所有依赖 jar 包可直接运行,瘦包仅含项目自身代码需依赖外部环境。
- 胖包(Fat Jar/War):也叫 uber-jar,打包时会将项目代码、所有依赖的第三方 jar 包、配置文件全部整合到一个归档文件中。
- 瘦包(Thin Jar/War):仅包含项目自身的编译代码和配置文件,不携带第三方依赖,依赖需从运行环境(如 Maven 仓库、服务器依赖目录)更据配置信息加载引用。
关键区别
- 体积与独立性:胖包体积大(几十 MB 到数百 MB),可独立运行无需额外依赖;瘦包体积小(几 MB 以内),运行需依赖外部依赖环境。
- 部署与维护:胖包部署简单,直接上传运行即可;瘦包需提前配置依赖环境,但更新项目时无需重复上传依赖,效率更高。
- 适用场景:胖包适合单机部署、快速测试、微服务独立实例;瘦包适合集群部署、容器化(如 Docker)、依赖共享的分布式环境。
Maven 打包实现方式
- 胖包实现 :常用
maven-assembly-plugin或spring-boot-maven-plugin(Spring Boot 项目),配置打包时包含依赖。 - 瘦包实现 :使用默认的
maven-jar-plugin或maven-war-plugin,不额外配置依赖打包,仅输出项目自身代码。
具体实现
打包的方式其实各种各样,本质是通过打包的插件实现,也可以自己写插件自定义打包。
胖包(maven-assembly-plugin)
xml
<plugin> <!--maven官方胖包打包插件-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version> <!--插件版本-->
<configuration>
<archive>
<manifest>
<mainClass>com.xxg.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
核心配置
-
<archive>:配置生成 Jar 包的元数据(如清单文件MANIFEST.MF) -
archive > manifest:配置清单文件MANIFEST.MF -
archive > manifest > mainClass:指定 Jar 包的主类,确保通过java -jar 包名.jar命令可直接运行 -
<descriptorRefs>:引用插件内置的打包规则(描述符) -
descriptorRefs > descriptorRef:配置其中一条打包规则 -
jar-with-dependencies:插件内置的关键规则,表示打包时会将:项目自身编译后的 .class 文件和所有依赖的第三方 Jar 包(包括
compile、runtime范围的依赖)全部合并到一个 Jar 包中,形成 "胖包"。
效果
配置后,执行 mvn assembly:assembly 命令会生成两个 Jar 包:
- 普通瘦包(仅项目自身代码,如
xxx-1.0-SNAPSHOT.jar)。 - 胖包(含所有依赖,如
xxx-1.0-SNAPSHOT-jar-with-dependencies.jar)。
胖包可直接通过 java -jar 胖包文件名.jar 运行,无需额外手动添加依赖。
胖包(spring-boot-maven-plugin)
SpringBoot项目打包最常用且最简单的方式是用SpringBoot的打包plugin。
spring-boot-maven-plugin 是 Spring Boot 官方提供的 Maven 插件,专为 Spring Boot项目设计,核心作用是将项目打包为可直接运行的 Spring Boot 专属胖包(Fat Jar/War) ,同时集成了 Spring Boot 特有的打包、运行、依赖管理等功能。
xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
在pom中加入此插件,再点击maven [install]或[repackge]就会把当前项目里所有依赖包和当前项目的源码都打成一个JAR包,同时还会将没有依赖包的JAR包也打出来。
核心特点
-
生成可直接运行的 Fat Jar
打包后包含项目自身代码、所有第三方依赖(
compile、runtime范围的依赖,自动处理依赖冲突)、Spring Boot 启动器(spring-boot-loader),无需额外配置依赖环境,直接通过java -jar 包名.jar运行。 -
特殊的 Jar 结构
与普通胖包不同,其内部采用分层结构:
-
BOOT-INF/classes/:存放项目自身编译 后的.class文件和配置文件。 -
BOOT-INF/lib/:存放所有第三方依赖 Jar 包。 -
META-INF/:包含清单文件(MANIFEST.MF),已指定Spring Boot启动类(
org.springframework.boot.loader.JarLauncher)。这种结构由spring-boot-loader组件负责解析,支持嵌套Jar加载,解决了普通Fat Jar的类加载冲突问题。
-
-
内置项目运行功能
支持通过
mvn spring-boot:run命令直接启动项目,无需先打包,方便开发调试。 -
支持分层打包(Layered Jar)
可将依赖、资源、应用代码等拆分为不同层,配合 Docker 构建时实现层缓存,加速镜像构建(Spring Boot 2.3+ 支持)。
痩包(maven-jar-plugin)
或者maven-war-plugin,这是Maven默认的打包方式,无需额外指定。直接打包,不打包依赖包,仅打包出项目中的代码到JAR包中。
设置lib目录
痩包通常需要借助外部的环境,常通过pom.xml配置 MANIFEST.MF 清单文件,指定类路径 和主类,方便运行时加载外部依赖。
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.yourpakagename.mainClassName</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
核心配置
archive > manifest:配置 Jar 包的清单文件(MANIFEST.MF)<addClasspath>true</addClasspath>:自动将项目的依赖Jar包名 添加到清单文件的Class-Path中。
例如:若项目依赖 spring-core-5.3.0.jar,则 MANIFEST.MF 中会生成:Class-Path: ~spring-core-5.3.0.jar ...。
~表示路径的前缀,可通过<classpathPrefix>配置。
-
<classpathPrefix>lib/</classpathPrefix>:指定依赖Jar包的存放路径前缀。结合上一条,生成的
Class-Path会自动加上lib/前缀,即运行环境中,依赖需放在与当前Jar包同级的lib目录下。(若不配置,默认依赖需与
Jar包放在同一目录,配置后更便于依赖管理)。 -
mainClass>com.yourpakagename.mainClassName</mainClass:指定 Jar 包的主类(包含main方法的类),确保可以通过java -jar 包名.jar直接运行。
例如:com.example.App 表示主类全路径,会在 MANIFEST.MF 中生成:Main-Class: com.example.App。
效果
配置后,执行 mvn package 会生成一个瘦包 (仅含项目自身的 .class 和配置文件),同时 MANIFEST.MF 中会包含:
Main-Class: com.yourpakagename.mainClassName
Class-Path: lib/xxx1.jar lib/xxx2.jar ...
依赖复制
上述痩包的打包,必须手动提供依赖 (否则运行时会报 ClassNotFoundException)。可以配合 maven-dependency-plugin 自动将依赖复制到 lib 目录。
xml
<plugins>
<!-- 生成瘦包并配置清单 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.yourpakagename.mainClassName</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!-- 添加插件,自动将依赖复制到 target/lib 目录 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id> <!--指定执行单元唯一标识,方便日志查看-->
<phase>package</phase> <!--绑定到那个生命周期执行,绑定后可自动执行-->
<goals>
<goal>copy-dependencies</goal> <!--指定当前执行单元要运行的插件目标(goal)为复制依赖-->
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 依赖输出目录 -->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<!--
${project.build.directory}是 Maven 内置的预定义属性,其值指向项目构建的输出目录,默认路径为:项目根目录/target。
-->
执行 mvn package 后,target 目录会生成:
-
瘦包(如
xxx-1.0.jar) -
lib目录(包含所有依赖 Jar 包)此时可直接通过
java -jar xxx-1.0.jar运行(依赖已在lib目录中,符合清单文件配置)。