Maven 依赖分离

Maven 依赖分离

Maven 依赖分离是指将项目的**核心代码(主 Jar)第三方依赖库(依赖 Jar)**分离到不同目录,而非打包在同一个文件中。这种方式常用于优化部署、减少重复传输、或配合 Docker 镜像分层构建。

以下是两种最常用的实现方案:


方案一:使用 maven-dependency-plugin(通用方案)

适用于所有 Maven 项目,通过插件将依赖复制到指定目录,并配置主 Jar 的 Class-Path 指向该目录。

1. 配置 pom.xml

<build><plugins> 中添加以下两个插件:

  1. 复制依赖到 target/lib 目录
  2. 配置主 Jar 的 Class-Path 和 Main-Class
xml 复制代码
<project>
    <!-- ... 其他配置 ... -->

    <build>
        <plugins>
            <!-- 1. 复制依赖到 target/lib 目录 -->
            <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> <!-- 绑定到 package 阶段 -->
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!-- 依赖输出目录 -->
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <!-- 是否包含传递性依赖 -->
                            <includeScope>runtime</includeScope>
                            <!-- 保持依赖的原有文件名 -->
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- 2. 配置主 Jar 的 Class-Path 和 Main-Class -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <!-- 程序入口类 -->
                            <mainClass>com.example.YourMainClass</mainClass>
                            <!-- 自动添加 Class-Path -->
                            <addClasspath>true</addClasspath>
                            <!-- Class-Path 中依赖的前缀目录 -->
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

** 关键配置说明 **

配置项 作用
<includeScope>runtime</includeScope> 仅复制运行时需要的依赖(compileruntime 范围),排除 test(测试用)和 provided(容器提供)的依赖。
<classpathPrefix>lib/</classpathPrefix> 告诉主 Jar 去 lib/ 目录下找依赖,必须与 outputDirectory 的目录名一致。
<useBaseVersion>true</useBaseVersion> 避免 SNAPSHOT 版本的依赖文件名带时间戳(如 1.0-20240101.123456-1.jar),保持文件名简洁。

2. 执行打包

运行 mvn clean package,你会在 target 目录下看到:

  • your-app.jar(你的主程序,体积很小)
  • lib/(包含所有依赖的 Jar 包)
3. 运行方式

your-app.jarlib/ 放在同一目录下,执行:

bash 复制代码
java -jar your-app.jar

方案二:Spring Boot 项目专用(spring-boot-maven-plugin

如果是 Spring Boot 项目,默认插件会把所有依赖打进一个 Fat Jar。若需分离,需调整插件配置。

1. 配置 pom.xml
xml 复制代码
<project>
    <!-- ... 其他配置 ... -->

    <build>
        <plugins>
            <!-- Spring Boot 插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 1. 启用分层打包(Layered Jars) -->
                    <layers>
                        <enabled>true</enabled>
                    </layers>
                    <!-- 或者 2. 使用 Properties 配置(旧版本方式) -->
                    <!-- <requiresUnpack>
                        <dependency>
                            <groupId>com.example</groupId>
                            <artifactId>some-large-lib</artifactId>
                        </dependency>
                    </requiresUnpack> -->
                </configuration>
            </plugin>

            <!-- 配合复制依赖插件(可选,若需物理分离) -->
            <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>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <includeScope>runtime</includeScope>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
2. Spring Boot 分层 Jar 说明

Spring Boot 的 layers 模式虽然不直接物理分离文件,但内部将 Jar 分为了不同层(如 dependenciesspring-boot-loadersnapshot-dependenciesapplication),非常适合 Docker 镜像构建,能利用缓存加速。

如果需要物理分离 (像方案一那样 lib 目录在外),建议直接使用 方案一 的通用配置,Spring Boot 项目同样兼容。


总结

  • 普通项目 :推荐 方案一,结构清晰,运行简单。
  • Spring Boot + Docker :推荐使用 layers 分层模式,优化镜像构建速度。
  • 注意 :分离后部署时,务必保证 lib 目录和主 Jar 的相对位置与 classpathPrefix 配置一致。

maven-dependency-plugin 介绍

maven-dependency-plugin 是 Maven 中处理依赖最核心的插件,提供了复制、解压、解析、分析项目依赖等功能,是实现依赖分离、离线构建、依赖冲突排查的关键工具。

以下是该插件的全面配置详解 ,结合核心 Goal(目标)和实际场景示例说明。


一、插件基本信息

pom.xml 中引入插件(建议使用最新稳定版):

xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>3.6.1</version> <!-- 请使用最新版本 -->
        </plugin>
    </plugins>
</build>

二、核心 Goal 及详细配置

该插件通过不同的 Goal(目标)实现不同功能,以下是最常用的 4 个 Goal

1. dependency:copy-dependencies(最常用:复制依赖到指定目录)

作用:将项目的依赖(包括传递性依赖)从本地仓库复制到指定目录,是实现"依赖分离"的核心 Goal。

常用配置参数
参数名 类型 默认值 说明
outputDirectory String ${project.build.directory}/dependency 依赖复制的目标目录(如 target/lib
includeScope String - 仅包含指定范围的依赖(如 runtimecompiletest
excludeScope String - 排除指定范围的依赖
includeGroupIds String - 仅包含指定 GroupId 的依赖(多个用逗号分隔)
excludeGroupIds String - 排除指定 GroupId 的依赖
includeArtifactIds String - 仅包含指定 ArtifactId 的依赖
excludeArtifactIds String - 排除指定 ArtifactId 的依赖
includeTypes String jar 仅包含指定类型的依赖(如 jarwarpom
classifier String - 仅包含指定 classifier 的依赖(如 sourcesjavadoc
overWriteReleases boolean false 是否覆盖已存在的 Release 版本依赖
overWriteSnapshots boolean true 是否覆盖已存在的 Snapshot 版本依赖
overWriteIfNewer boolean true 仅当依赖文件更新时才覆盖
useBaseVersion boolean false 文件名使用基础版本(如 1.0 而非 1.0-20240101.123456-1
stripVersion boolean false 复制的文件名去掉版本号(如 commons-lang3.jar
prependGroupId boolean false 文件名前加上 GroupId 前缀(如 org.apache.commons-commons-lang3.jar
copyPom boolean false 是否同时复制依赖的 .pom 文件
failOnMissingClassifierArtifact boolean true 当找不到指定 classifier 的依赖时是否构建失败
完整配置示例(依赖分离场景)
xml 复制代码
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>3.6.1</version>
    <executions>
        <execution>
            <id>copy-libs</id>
            <phase>package</phase> <!-- 绑定到 package 阶段 -->
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <!-- 1. 目标目录:target/lib -->
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                
                <!-- 2. 仅包含 runtime 范围的依赖(排除 test、provided) -->
                <includeScope>runtime</includeScope>
                
                <!-- 3. 排除不需要的依赖(如 lombok) -->
                <excludeGroupIds>org.projectlombok</excludeGroupIds>
                
                <!-- 4. 优化文件覆盖策略 -->
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>true</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
                
                <!-- 5. 文件名使用基础版本(避免 SNAPSHOT 时间戳) -->
                <useBaseVersion>true</useBaseVersion>
            </configuration>
        </execution>
    </executions>
</plugin>

2. dependency:unpack(解压依赖)

作用:将依赖的 Jar/War 包解压到指定目录(常用于提取依赖中的资源文件、 native 库等)。

特有配置参数(除了 copy-dependencies 的参数外)
参数名 类型 说明
includes String 仅解压指定文件(Ant 风格路径,如 **/*.xml
excludes String 排除指定文件
配置示例(提取依赖中的配置文件)
xml 复制代码
<execution>
    <id>unpack-configs</id>
    <phase>generate-resources</phase>
    <goals>
        <goal>unpack</goal>
    </goals>
    <configuration>
        <outputDirectory>${project.build.directory}/classes/config</outputDirectory>
        <!-- 仅解压某个特定依赖 -->
        <includeGroupIds>com.example</includeGroupIds>
        <includeArtifactIds>my-common-lib</includeArtifactIds>
        <!-- 仅解压 xml 和 properties 文件 -->
        <includes>**/*.xml,**/*.properties</includes>
    </configuration>
</execution>

3. dependency:resolve(解析依赖)

作用:确保所有依赖都下载到本地仓库,不做复制/解压操作,常用于构建前的预检查或离线构建准备。

配置示例
xml 复制代码
<execution>
    <id>resolve-deps</id>
    <phase>validate</phase>
    <goals>
        <goal>resolve</goal>
    </goals>
    <configuration>
        <!-- 解析所有范围的依赖(包括 test) -->
        <includeScope>test</includeScope>
    </configuration>
</execution>

4. dependency:analyze(分析依赖)

作用:分析项目的依赖使用情况,找出:

  • unused declared dependencies:声明了但未使用的依赖
  • used undeclared dependencies:使用了但未直接声明的依赖(传递性依赖)
常用配置参数
参数名 类型 默认值 说明
failOnWarning boolean false 发现上述问题时是否构建失败
ignoreNonCompile boolean false 忽略 testprovidedruntime 范围的依赖
usedDependencies String - 强制声明某些依赖是"已使用"的(避免误报)
配置示例(构建时检查未使用依赖)
xml 复制代码
<execution>
    <id>analyze-deps</id>
    <phase>verify</phase>
    <goals>
        <goal>analyze-only</goal> <!-- 注意:用 analyze-only 而非 analyze,避免重复执行 -->
    </goals>
    <configuration>
        <!-- 发现未使用依赖时构建失败(强制清理无用依赖) -->
        <failOnWarning>true</failOnWarning>
        <!-- 忽略 test 范围 -->
        <ignoreNonCompile>true</ignoreNonCompile>
        <!-- 强制认为 lombok 是已使用的(避免误报) -->
        <usedDependencies>
            <usedDependency>org.projectlombok:lombok</usedDependency>
        </usedDependencies>
    </configuration>
</execution>

三、高级应用场景

1. 离线构建准备(dependency:go-offline

如果需要在无网络环境下构建项目,可先执行:

bash 复制代码
mvn dependency:go-offline

该命令会下载所有依赖(包括插件及其依赖)到本地仓库,配合 -o(offline)参数即可离线构建:

bash 复制代码
mvn clean package -o
2. 结合 Profile 实现环境差异化依赖
xml 复制代码
<profiles>
    <profile>
        <id>prod</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>copy-prod-libs</id>
                            <phase>package</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.directory}/prod-lib</outputDirectory>
                                <!-- 生产环境排除 test 依赖 -->
                                <excludeScope>test</excludeScope>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

四、注意事项

  1. 版本兼容性:确保插件版本与 Maven 核心版本兼容(Maven 3.6+ 建议使用 3.2.0+ 版本的插件)。
  2. 传递性依赖includeScope 会同时影响传递性依赖(如 includeScope=runtime 会包含 compile 范围的传递性依赖,因为 Maven 会将其转为 runtime)。
  3. 性能优化 :如果依赖数量很多,可通过 excludeGroupIds 排除不需要的组(如 org.apache.maven),提升复制速度。
相关推荐
重庆兔巴哥2 小时前
如何安装和配置Java开发环境(JDK)?
java·开发语言
鸽鸽程序猿2 小时前
【Java EE】【SpringAI】智能聊天机器人
java·java-ee·机器人
sthnyph2 小时前
Spring Framework 中文官方文档
java·后端·spring
带刺的坐椅2 小时前
Snack4 Json 流式解析与自动结构修复深度指南
java·llm·json·jsonpath
zb200641202 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
我命由我123452 小时前
Android 多进程开发 - FileDescriptor、Uri、AIDL 接口定义不能抛出异常
android·java·java-ee·kotlin·android studio·android-studio·android runtime
xyhuix2 小时前
Spring+Quartz实现定时任务的配置方法
java
分享牛2 小时前
Operaton入门到精通22-Operaton 2.0 升级指南:Spring Boot 4 核心变更详解
java·spring boot·后端
jinanmichael2 小时前
SpringBoot 如何调用 WebService 接口
java·spring boot·后端