平时开发、写小工具、本地调试时,经常会遇到一个实用需求:根据 Maven 坐标(groupId、artifactId、version),自动把 Jar 包下载到本地。
用过 DBeaver 的朋友应该都有印象:新建数据库连接时,工具会自动下载对应的 MySQL、PostgreSQL、Oracle 驱动,不用我们手动找包、导入配置,全程无感、非常方便。
很多人好奇它的底层实现:到底是调用了本地 Maven 命令?还是简单的 HTTP 文件下载?
这篇文章就用通俗直白的方式,分享 3 种 Java 下载 Maven Jar 的实战方案,对比各自的优缺点和适用场景,同时拆解 DBeaver 原生实现原理。所有代码都是最新稳定版本,无老旧依赖、无版本冲突,可直接复制复用。
一、三种实现方案整体对比
目前 Java 程序动态拉取 Maven Jar 包,主流就三种实现方式,适配不同场景,大家可以按需选用:
- 本地 Maven 命令调用:代码极简,零额外依赖,但必须本地安装配置 Maven
- 轻量 HTTP 直连下载:无需 Maven 环境、依赖轻便,只适合下载独立单 Jar,不支持依赖传递
- 内嵌 Eclipse Aether 内核:DBeaver、IDEA 同款方案,无需本地 Maven,完整支持 Maven 所有核心能力,是专业工具的首选
二、方案一:调用本地 Maven 命令(最简临时方案)
1. 实现思路
直接通过 Java 执行系统命令,调用本地 Maven 的 dependency:get 指令,根据坐标拉取对应的 Jar 包,属于拿来即用的临时方案。
2. 完整可运行代码
import java.io.File;
import java.io.IOException;
/**
* 本地Maven命令下载Jar
* 适合:本地调试、本机已配置Maven环境
*/
public class MvnCmdDownloadUtil {
/**
* @param groupId 项目组织ID
* @param artifactId 项目名称
* @param version 版本号
* @param saveDir Jar保存目录
*/
public static void downloadJarByMvnCmd(String groupId, String artifactId, String version, String saveDir) throws IOException, InterruptedException {
File dir = new File(saveDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 下载命令:transitive=false 仅下载当前Jar,不拉取依赖包
String[] cmd = new String[]{
"mvn",
"dependency:get",
"-DgroupId=" + groupId,
"-DartifactId=" + artifactId,
"-Dversion=" + version,
"-Ddest=" + saveDir,
"-Dtransitive=false"
};
// 执行命令并打印日志
Process process = new ProcessBuilder(cmd)
.inheritIO()
.directory(new File(saveDir))
.start();
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("Jar下载失败,命令退出码:" + exitCode);
}
System.out.println("Jar包下载完成,保存目录:" + saveDir);
}
public static void main(String[] args) throws Exception {
// 测试下载 fastjson2
downloadJarByMvnCmd("com.alibaba.fastjson2", "fastjson2", "2.0.32", "D:/java-libs");
}
}
3. 方案优劣
✅ 优点:无需引入任何第三方依赖、代码极少、上手零成本
❌ 缺点:强依赖本地 Maven 环境,未配置 Maven 的设备无法运行,不适合线上项目和通用工具开发
三、方案二:轻量 HTTP 下载(无Maven依赖、轻量化首选)
1. 实现思路
Maven 仓库的文件有固定的 URL 规则,我们可以直接拼接仓库地址和 Maven 坐标路径,通过 HTTP 请求下载 Jar 包。全程不依赖 Maven 内核,依赖包轻量化、无版本冲突。
2. 项目依赖(新版稳定无冲突)
<!-- 新版HTTP客户端,替代老旧HttpClient4 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
<!-- 便捷文件工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
3. 完整可运行代码
import org.apache.commons.io.FileUtils;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import java.io.File;
import java.net.URI;
/**
* 轻量化Maven Jar下载工具
* 适合:仅下载单个独立Jar、追求低依赖、避免版本冲突的场景
*/
public class LightMavenDownloadUtil {
// 阿里云Maven镜像(国内下载加速,替代官方中央仓库)
private static final String MAVEN_ALI_REPO = "https://maven.aliyun.com/repository/public";
public static File downloadJar(String groupId, String artifactId, String version, String targetDir) throws Exception {
// 拼接标准Maven仓库路径
String groupPath = groupId.replace(".", "/");
String jarUrl = String.format("%s/%s/%s/%s/%s-%s.jar",
MAVEN_ALI_REPO, groupPath, artifactId, version, artifactId, version);
// 创建本地保存目录
File saveDir = new File(targetDir);
if (!saveDir.exists()) {
saveDir.mkdirs();
}
File jarFile = new File(saveDir, artifactId + "-" + version + ".jar");
// 执行HTTP下载
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet(URI.create(jarUrl));
httpClient.execute(httpGet, response -> {
FileUtils.copyInputStreamToFile(response.getEntity().getContent(), jarFile);
return null;
});
}
return jarFile;
}
public static void main(String[] args) throws Exception {
File jarFile = downloadJar("com.alibaba.fastjson2", "fastjson2", "2.0.32", "D:/java-libs");
System.out.println("轻量下载完成,文件路径:" + jarFile.getAbsolutePath());
}
}
4. 方案优劣
✅ 优点:依赖少、版本新、无冲突、下载速度快,完全不依赖 Maven 环境
❌ 缺点:不支持依赖传递解析,只会下载指定的单个 Jar;无法识别 RELEASE 动态版本、带 classifier 的特殊 Jar 包
四、方案三:内嵌Aether内核(DBeaver 同款专业实现)
这是 IDEA、DBeaver 等主流 Java 工具的底层实现方案,也是生产级开发的最优选择。通过内嵌 Maven 官方解析内核,不用本地安装 Maven,就能完整复刻 Maven 的所有依赖处理能力。
1. 核心优势
支持依赖树解析、自动下载传递依赖、自定义私服/镜像、动态版本、Jar 分类器、本地缓存等完整 Maven 特性,完美适配 JDBC 驱动、插件包等复杂 Jar 下载场景。
2. 最新稳定依赖(适配 JDK8~JDK21)
摒弃网上老旧的 1.4.2、3.8.6 旧版本,修复 HTTPS 证书、镜像超时、连接失败等常见问题:
<!-- 新版Aether Maven解析内核 -->
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-api</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-impl</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-connector-basic</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-transport-file</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.eclipse.aether</groupId>
<artifactId>aether-transport-http</artifactId>
<version>1.4.4</version>
</dependency>
<!-- 适配新版Maven核心 -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-aether-provider</artifactId>
<version>3.9.6</version>
</dependency>
3. 完整工具类代码
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import java.io.File;
import java.util.Collections;
/**
* DBeaver同款Jar下载工具
* 内嵌Aether内核,完整复刻Maven核心能力
*/
public class AetherMavenDownloadUtil {
// 初始化Maven解析内核
private static RepositorySystem newRepositorySystem() {
DefaultServiceLocator locator = new DefaultServiceLocator();
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
return locator.getService(RepositorySystem.class);
}
// 创建仓库会话,配置本地缓存路径
private static RepositorySystemSession newSession(RepositorySystem system, String localRepoPath) {
DefaultRepositorySystemSession session = new DefaultRepositorySystemSession();
LocalRepository localRepo = new LocalRepository(localRepoPath);
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
return session;
}
// 核心下载方法:自动解析依赖、下载所有关联Jar
public static File downloadJar(String groupId, String artifactId, String version, String localRepoPath) throws Exception {
RepositorySystem system = newRepositorySystem();
RepositorySystemSession session = newSession(system, localRepoPath);
// 配置阿里云镜像加速
RemoteRepository aliRepo = new RemoteRepository.Builder(
"aliyun",
"default",
"https://maven.aliyun.com/repository/public"
).build();
// 构建Maven坐标并发起解析请求
Artifact artifact = new DefaultArtifact(groupId + ":" + artifactId + ":" + version);
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(new org.eclipse.aether.graph.Dependency(artifact, "compile"));
collectRequest.setRepositories(Collections.singletonList(aliRepo));
// 解析依赖并下载所有相关Jar包
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
org.eclipse.aether.resolution.DependencyResult result = system.resolveDependencies(session, dependencyRequest);
// 匹配目标Jar文件并返回
for (ArtifactResult artifactResult : result.getArtifactResults()) {
Artifact art = artifactResult.getArtifact();
if (art.getGroupId().equals(groupId) && art.getArtifactId().equals(artifactId)) {
return art.getFile();
}
}
throw new RuntimeException("未匹配到对应Jar文件,请检查Maven坐标是否正确");
}
public static void main(String[] args) throws Exception {
// 自定义本地仓库缓存目录
String localRepo = "D:/maven-local-repo";
File jarFile = downloadJar("com.alibaba.fastjson2", "fastjson2", "2.0.32", localRepo);
System.out.println("专业内核下载完成,Jar路径:" + jarFile.getAbsolutePath());
}
}
五、硬核拆解:DBeaver 到底用的哪种方案?
直接说结论:DBeaver 全程使用内嵌 Aether 内核方案,和上面第三种代码逻辑完全一致,这也是专业 Java 工具的标准答案。
1. 为什么不用另外两种方案?
- 舍弃 Maven 命令行:DBeaver 是绿色免安装工具,不可能要求用户本地必须安装、配置 Maven,兼容性太差
- 舍弃简易 HTTP 下载:绝大多数 JDBC 驱动都有多层传递依赖,只下载单个 Jar 会导致驱动运行报错;同时无法适配动态版本、特殊分类器的驱动包
2. DBeaver 真实下载流程
- 读取内置驱动配置,获取对应数据库驱动的标准 Maven 坐标
- 调用内置 Aether 内核,加载用户配置的 Maven 镜像/私服地址
- 优先读取本地缓存仓库(默认路径:
%APPDATA%\DBeaverData\drivers\maven\),有缓存直接复用,无需重复下载 - 无缓存则远程拉取 Pom 文件,递归解析、下载所有依赖 Jar 包
- 通过独立类加载器动态挂载 Jar,无需重启工具即可正常使用数据库驱动
六、方案选型指南(直接照着选即可)
| 实现方案 | 适用场景 | 推荐指数 |
|---|---|---|
| 本地Maven命令下载 | 本地临时调试、设备已配置Maven环境 | ⭐⭐ |
| 轻量HTTP下载 | 仅下载独立单Jar、追求轻量化、规避依赖冲突 | ⭐⭐⭐⭐ |
| 内嵌Aether内核 | 工具开发、动态加载驱动、需要解析完整依赖树、线上正式项目 | ⭐⭐⭐⭐⭐ |
七、实战避坑小结
整理几个实际开发中高频遇到的问题,帮大家少走弯路:
- 尽量避开老旧的 Aether1.4.2、Maven3.8.6 版本,大概率出现 HTTPS 证书异常、镜像超时等问题,优先使用文中新版依赖
- 如果需要下载 JDBC 驱动、插件等带依赖的 Jar 包,必须用 Aether 方案,轻量 HTTP 方案无法满足依赖解析需求
- 国内开发统一使用阿里云 Maven 镜像,能大幅提升下载速度,避免中央仓库连接超时、下载失败
- SpringBoot 项目优先选用轻量 HTTP 方案,可有效规避 Maven 内核带来的依赖冲突

