Maven 分模块打包实战 核心概念 在多模块项目中,不同模块的打包方式和目标不同:
父模块 :不打包,仅用于聚合
公共模块 :打包成普通 JAR,供其他模块依赖
服务模块 :打包成普通 JAR 或可执行 JAR
启动模块 :打包成可执行 JAR,包含所有依赖
打包类型对比
打包类型
packaging
产物
适用场景
大小
POM
pom
无 JAR
父模块、聚合模块
-
普通 JAR
jar
仅包含本模块代码
公共模块、被依赖模块
小(几十 KB - 几 MB)
可执行 JAR
jar
包含所有依赖
启动模块、独立应用
大(几十 MB - 几百 MB)
WAR
war
Web 应用包
部署到 Tomcat 等容器
中等
父模块打包配置 基本配置 1 2 3 4 5 6 7 8 9 10 11 12 13 <project > <groupId > com.example</groupId > <artifactId > my-project</artifactId > <version > 1.0.0</version > <packaging > pom</packaging > <modules > <module > my-common</module > <module > my-service</module > <module > my-app</module > </modules > </project >
特点 :
packaging 必须是 pom
不会生成 JAR 文件
执行 mvn package 时会构建所有子模块
公共模块打包配置 公共模块(如 common、domain、repository)被其他模块依赖,只需打包成普通 JAR。
基本配置 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 <project > <parent > <groupId > com.example</groupId > <artifactId > my-project</artifactId > <version > 1.0.0</version > </parent > <artifactId > my-common</artifactId > <packaging > jar</packaging > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 3.3.0</version > <configuration > <archive > <manifest > <addDefaultImplementationEntries > true</addDefaultImplementationEntries > </manifest > </archive > </configuration > </plugin > </plugins > </build > </project >
打包产物 1 2 my-common/target/ └── my-common-1.0.0.jar
使用方式 :
1 2 3 4 5 6 7 8 mvn clean install <dependency> <groupId>com.example</groupId> <artifactId>my-common</artifactId> </dependency>
启动模块打包配置 启动模块需要打包成可执行 JAR ,包含所有依赖,可独立运行。
方式一:Spring Boot Maven Plugin(推荐) 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 <project > <parent > <groupId > com.example</groupId > <artifactId > my-project</artifactId > <version > 1.0.0</version > </parent > <artifactId > my-app</artifactId > <packaging > jar</packaging > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > my-service</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > 3.2.0</version > <configuration > <mainClass > com.example.MyApplication</mainClass > <excludes > <exclude > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </exclude > </excludes > </configuration > <executions > <execution > <goals > <goal > repackage</goal > </goals > </execution > </executions > </plugin > </plugins > </build > </project >
关键配置 :
repackage goal:将依赖打包到 JAR 中
自动配置 MANIFEST.MF 中的 Main-Class
使用 Spring Boot 的类加载器
打包产物 :
1 2 3 my-app/target/ ├── my-app-1.0.0.jar └── my-app-1.0.0.jar.original
运行 :
1 java -jar target/my-app-1.0.0.jar
方式二:Maven Assembly Plugin(自定义打包) 适用于非 Spring Boot 项目或需要自定义打包结构。
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 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-assembly-plugin</artifactId > <version > 3.6.0</version > <configuration > <archive > <manifest > <mainClass > com.example.Main</mainClass > </manifest > </archive > <descriptorRefs > <descriptorRef > jar-with-dependencies</descriptorRef > </descriptorRefs > </configuration > <executions > <execution > <id > make-assembly</id > <phase > package</phase > <goals > <goal > single</goal > </goals > </execution > </executions > </plugin > </plugins > </build >
打包产物 :
1 target/my-app-1.0.0-jar-with-dependencies.jar
运行 :
1 java -jar target/my-app-1.0.0-jar-with-dependencies.jar
方式三:Maven Shade Plugin(合并依赖) 适用于需要解决依赖冲突或重命名包的场景。
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 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-shade-plugin</artifactId > <version > 3.5.0</version > <configuration > <transformers > <transformer implementation ="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer" > <mainClass > com.example.Main</mainClass > </transformer > <transformer implementation ="org.apache.maven.plugins.shade.resource.AppendingTransformer" > <resource > META-INF/spring.handlers</resource > </transformer > <transformer implementation ="org.apache.maven.plugins.shade.resource.AppendingTransformer" > <resource > META-INF/spring.schemas</resource > </transformer > </transformers > <filters > <filter > <artifact > *:*</artifact > <excludes > <exclude > META-INF/*.SF</exclude > <exclude > META-INF/*.DSA</exclude > <exclude > META-INF/*.RSA</exclude > </excludes > </filter > </filters > </configuration > <executions > <execution > <phase > package</phase > <goals > <goal > shade</goal > </goals > </execution > </executions > </plugin > </plugins > </build >
特点 :
可以重命名包(relocate)避免冲突
可以合并 META-INF 下的配置文件
生成的 JAR 替换原始 JAR
瘦 JAR + lib 目录方式 有些场景下不希望所有依赖打包到一个 JAR 中,可以采用瘦 JAR + lib 目录的方式。
配置 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 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-dependency-plugin</artifactId > <version > 3.6.0</version > <executions > <execution > <id > copy-dependencies</id > <phase > package</phase > <goals > <goal > copy-dependencies</goal > </goals > <configuration > <outputDirectory > ${project.build.directory}/lib</outputDirectory > <includeScope > runtime</includeScope > </configuration > </execution > </executions > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 3.3.0</version > <configuration > <archive > <manifest > <addClasspath > true</addClasspath > <classpathPrefix > lib/</classpathPrefix > <mainClass > com.example.Main</mainClass > </manifest > </archive > </configuration > </plugin > </plugins > </build >
打包产物 1 2 3 4 5 6 7 target/ ├── my-app-1.0.0.jar └── lib/ ├── my-common-1.0.0.jar ├── my-service-1.0.0.jar ├── spring-boot-3.2.0.jar └── ...
运行 1 2 3 4 5 java -jar target/my-app-1.0.0.jar java -cp "target/my-app-1.0.0.jar:target/lib/*" com.example.Main
优点 :
更新单个模块时,只需替换对应的 JAR
依赖可复用,多个应用共享
JAR 文件小,传输快
缺点 :
常用打包命令 基本命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mvn clean package -DskipTests mvn clean install mvn clean package -pl my-app mvn clean package -pl my-app -am mvn clean package -pl my-common,my-service mvn clean package -pl !my-test mvn clean package -T 4
多环境打包 使用 Maven Profile 实现多环境打包:
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 <profiles > <profile > <id > dev</id > <activation > <activeByDefault > true</activeByDefault > </activation > <properties > <env > dev</env > <spring.profiles.active > dev</spring.profiles.active > </properties > </profile > <profile > <id > test</id > <properties > <env > test</env > <spring.profiles.active > test</spring.profiles.active > </properties > </profile > <profile > <id > prod</id > <properties > <env > prod</env > <spring.profiles.active > prod</spring.profiles.active > </properties > </profile > </profiles >
打包命令 :
1 2 3 4 5 6 7 8 9 10 11 mvn clean package mvn clean package -Ptest mvn clean package -Pprod mvn clean package -Ptest,docker
资源过滤 :
1 2 3 4 5 6 7 8 9 10 11 12 <build > <resources > <resource > <directory > src/main/resources</directory > <filtering > true</filtering > <includes > <include > application.yml</include > <include > application-${env}.yml</include > </includes > </resource > </resources > </build >
打包优化 1. 排除不必要的依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <exclusions > <exclusion > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > </exclusion > </exclusions > </dependency >
2. 依赖分析 1 2 3 4 5 6 7 8 9 10 11 mvn dependency:tree mvn dependency:tree -Dverbose mvn dependency:list mvn dependency:analyze
3. 压缩 JAR 包 1 2 3 4 5 6 7 8 9 10 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <configuration > <archive > <compress > true</compress > <index > true</index > </archive > </configuration > </plugin >
4. 跳过不必要的步骤 1 2 3 4 5 6 7 8 mvn clean package -DskipTests mvn clean package -Dmaven.test.skip=true mvn clean package -Dmaven.javadoc.skip=true
Docker 镜像打包 方式一:Dockerfile + Maven 打包 1. Maven 打包可执行 JAR 1 mvn clean package -DskipTests
2. 编写 Dockerfile 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 FROM eclipse-temurin:17 -jdk-alpine AS builderWORKDIR /app COPY pom.xml . COPY my-common ./my-common COPY my-service ./my-service COPY my-app ./my-app RUN ./mvnw clean package -DskipTests FROM eclipse-temurin:17 -jre-alpineWORKDIR /app COPY --from=builder /app/my-app/target/my-app-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT ["java" , "-jar" , "app.jar" ]
简化版(本地已打包) :
1 2 3 4 5 FROM eclipse-temurin:17 -jre-alpineWORKDIR /app COPY my-app/target/my-app-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT ["java" , "-jar" , "app.jar" ]
3. 构建镜像 1 2 3 4 5 docker build -t my-app:1.0.0 . docker run -d -p 8080:8080 my-app:1.0.0
方式二:Jib Maven Plugin(无需 Dockerfile) Jib 是 Google 开源的 Maven/Gradle 插件,无需 Dockerfile 即可构建 Docker 镜像。
配置 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 <build > <plugins > <plugin > <groupId > com.google.cloud.tools</groupId > <artifactId > jib-maven-plugin</artifactId > <version > 3.4.0</version > <configuration > <from > <image > eclipse-temurin:17-jre-alpine</image > </from > <to > <image > my-app</image > <tags > <tag > 1.0.0</tag > <tag > latest</tag > </tags > </to > <container > <jvmFlags > <jvmFlag > -Xms512m</jvmFlag > <jvmFlag > -Xmx1024m</jvmFlag > </jvmFlags > <ports > <port > 8080</port > </ports > <mainClass > com.example.MyApplication</mainClass > <creationTime > USE_CURRENT_TIMESTAMP</creationTime > </container > </configuration > </plugin > </plugins > </build >
构建命令 1 2 3 4 5 6 7 8 mvn compile jib:dockerBuild mvn compile jib:build mvn compile jib:buildTar
优点 :
无需安装 Docker
无需编写 Dockerfile
构建速度快(增量构建)
自动优化镜像分层
方式三:Spring Boot Maven Plugin 构建镜像 Spring Boot 2.3+ 内置了镜像构建功能(使用 Cloud Native Buildpacks)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <image > <name > my-app:${project.version}</name > <env > <BP_JVM_VERSION > 17</BP_JVM_VERSION > </env > </image > </configuration > </plugin > </plugins > </build >
构建命令 :
1 2 3 4 5 mvn spring-boot:build-image mvn spring-boot:build-image -Dspring-boot.build-image.publish=true
实战案例 场景 1:微服务项目打包 1 2 3 4 5 6 microservices-project/ ├── pom.xml ├── common/ ├── user-service/ ├── order-service/ └── gateway/
父 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 <properties > <spring-boot.version > 3.2.0</spring-boot.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > ${spring-boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <build > <pluginManagement > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > <executions > <execution > <goals > <goal > repackage</goal > </goals > </execution > </executions > </plugin > </plugins > </pluginManagement > </build >
公共模块 POM(common) 1 2 <artifactId > common</artifactId >
服务模块 POM(user-service) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <artifactId > user-service</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > common</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build >
打包命令 1 2 3 4 5 6 7 8 mvn clean package -DskipTests mvn clean package -pl user-service -am -DskipTests mvn clean package -Pprod -DskipTests
产物 1 2 3 4 common/target/common-1.0.0.jar user-service/target/user-service-1.0.0.jar order-service/target/order-service-1.0.0.jar gateway/target/gateway-1.0.0.jar
场景 2:框架项目打包(供第三方使用) 1 2 3 4 5 6 rag-framework/ ├── pom.xml ├── rag-core/ ├── rag-store-pgvector/ ├── rag-spring-boot-starter/ └── rag-app/
核心模块和实现模块 1 2 3 4 5 6 7 <artifactId > rag-core</artifactId > <artifactId > rag-store-pgvector</artifactId >
发布到 Maven 仓库 1 2 3 4 5 6 7 8 mvn clean install mvn clean deploy mvn clean deploy -P release
用户使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > <version > 1.0.0</version > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-store-pgvector</artifactId > <version > 1.0.0</version > </dependency > </dependencies >
常见问题 Q1: Spring Boot 打包后 JAR 很大怎么办? A : 几种优化方式:
排除不必要的依赖
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <exclusions > <exclusion > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > </exclusion > </exclusions > </dependency >
使用瘦 JAR + lib 目录
1 2 3 4 5 6 7 <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <layout > ZIP</layout > </configuration > </plugin >
使用 Docker 分层构建
Q2: 多模块打包时出现 “Could not resolve dependencies” 错误? A : 原因和解决方法:
模块未安装到本地仓库
1 2 mvn clean install -pl common -am
版本不一致
检查子模块版本是否与父 POM 一致
使用 ${project.version} 引用
构建顺序错误
Maven 会自动计算依赖顺序
使用 -am 参数构建依赖模块
Q3: 如何在打包时指定不同的配置文件? A : 使用 Maven Profile + 资源过滤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <profiles > <profile > <id > dev</id > <properties > <config.file > application-dev.yml</config.file > </properties > </profile > <profile > <id > prod</id > <properties > <config.file > application-prod.yml</config.file > </properties > </profile > </profiles > <build > <resources > <resource > <directory > src/main/resources</directory > <filtering > true</filtering > </resource > </resources > </build >
打包:
1 mvn clean package -Pprod
Q4: 如何生成源码 JAR 和 Javadoc JAR? A : 用于发布到 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 26 27 28 29 30 31 32 33 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-source-plugin</artifactId > <version > 3.3.0</version > <executions > <execution > <id > attach-sources</id > <goals > <goal > jar</goal > </goals > </execution > </executions > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-javadoc-plugin</artifactId > <version > 3.6.0</version > <executions > <execution > <id > attach-javadocs</id > <goals > <goal > jar</goal > </goals > </execution > </executions > </plugin > </plugins > </build >
Q5: 打包时如何跳过某些模块? A : 使用 -pl 和 ! 排除:
1 2 3 4 5 mvn clean package -pl !test-module mvn clean package -pl user-service,order-service
打包最佳实践 1. 模块职责清晰
模块类型
打包方式
发布
公共模块
普通 JAR
install 到本地仓库
服务模块
普通 JAR 或可执行 JAR
看是否独立运行
启动模块
可执行 JAR
部署
2. 统一管理插件版本 在父 POM 的 pluginManagement 中统一配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <pluginManagement > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${spring-boot.version}</version > </plugin > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 3.3.0</version > </plugin > </plugins > </pluginManagement >
3. 使用 Profile 管理多环境 1 2 3 4 5 6 7 8 9 10 11 <profiles > <profile > <id > dev</id > <activation > <activeByDefault > true</activeByDefault > </activation > </profile > <profile > <id > prod</id > </profile > </profiles >
4. 优化构建速度 1 2 3 4 5 6 7 8 mvn clean package -T 4 mvn clean package -DskipTests mvn clean package -o
5. 依赖管理 1 2 3 4 5 6 7 8 mvn dependency:analyze mvn dependency:tree -Dverbose mvn dependency:purge-local-repository
总结
打包方式
适用场景
命令
产物大小
普通 JAR
公共模块、被依赖模块
mvn package
小
可执行 JAR(Spring Boot)
启动模块、微服务
mvn package
大
瘦 JAR + lib
需要分离依赖的场景
mvn package
小 + lib 目录
Docker 镜像
容器化部署
mvn jib:dockerBuild
镜像
关键点 :
父模块 packaging=pom,不打包
公共模块打包成普通 JAR,install 到本地仓库
启动模块使用 Spring Boot Maven Plugin 打包成可执行 JAR
使用 Profile 管理多环境配置
使用 -pl 和 -am 精确控制打包范围