You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建可自动下载依赖的非Fat Java Jar文件

实现可执行Jar自动下载依赖(不生成Fat Jar)

需求:构建Java项目并打包为可执行Jar,不将依赖打包进Jar中,运行时自动下载所需依赖保证程序正常执行。现有build.gradle如下:

/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/8.0.1/userguide/building_java_projects.html
 */

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:31.1-jre'
    implementation 'com.pi4j:pi4j-core:2.3.0'
    // implementation 'com.pi4j:pi4j-ktx:2.3.0' // Kotlin DSL
    implementation("com.pi4j:pi4j-plugin-raspberrypi:2.2.1")
    implementation("com.pi4j:pi4j-plugin-pigpio:2.2.1")
}

application {
    // Define the main class for the application.
    mainClass = 'new_folder.App'
}

jar {
    manifest {
        attributes 'Main-Class': application.mainClass
    }
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

解决方案

1. 调整build.gradle配置

添加生成依赖清单的任务,修改Jar的主类为自定义启动器,并将真实业务主类存入Manifest:

plugins {
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
    implementation 'com.google.guava:guava:31.1-jre'
    implementation 'com.pi4j:pi4j-core:2.3.0'
    implementation("com.pi4j:pi4j-plugin-raspberrypi:2.2.1")
    implementation("com.pi4j:pi4j-plugin-pigpio:2.2.1")
}

application {
    mainClass = 'new_folder.App'
}

// 生成依赖清单文件,包含所有运行时依赖的坐标
task generateDependenciesList {
    doLast {
        def dependenciesFile = file("$buildDir/resources/main/dependencies.txt")
        dependenciesFile.parentFile.mkdirs()
        configurations.runtimeClasspath.resolvedConfiguration.resolvedArtifacts.each { artifact ->
            def module = artifact.moduleVersion.id
            dependenciesFile.append("${module.group}:${module.name}:${module.version}\n")
        }
    }
}

jar {
    dependsOn generateDependenciesList
    manifest {
        attributes 'Main-Class': 'new_folder.DependencyLoader', // 自定义启动器类
                   'App-Main-Class': application.mainClass // 存储真实业务主类
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

2. 编写依赖加载启动器DependencyLoader.java

该类负责读取依赖清单、下载依赖到本地、动态加载类并启动主程序:

package new_folder;

import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Manifest;

public class DependencyLoader {
    private static final String DEPENDENCIES_FILE = "/dependencies.txt";
    private static final String DEPENDENCY_DIR = "lib";

    public static void main(String[] args) throws Exception {
        // 创建本地依赖存储目录
        Path libDir = Paths.get(DEPENDENCY_DIR);
        Files.createDirectories(libDir);

        // 读取Jar内的依赖清单
        List<String> dependencies = new ArrayList<>();
        try (InputStream is = DependencyLoader.class.getResourceAsStream(DEPENDENCIES_FILE);
             BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
            String line;
            while ((line = br.readLine()) != null) {
                dependencies.add(line.trim());
            }
        }

        // 下载依赖到本地目录
        List<URL> dependencyUrls = new ArrayList<>();
        for (String dep : dependencies) {
            String[] parts = dep.split(":");
            String group = parts[0];
            String artifact = parts[1];
            String version = parts[2];

            // 构建Maven Central下载路径
            String urlStr = String.format("https://repo1.maven.org/maven2/%s/%s/%s/%s-%s.jar",
                    group.replace('.', '/'), artifact, version, artifact, version);
            Path destPath = libDir.resolve(String.format("%s-%s.jar", artifact, version));

            // 依赖文件不存在则执行下载
            if (!Files.exists(destPath)) {
                try (InputStream in = new URL(urlStr).openStream();
                     OutputStream out = Files.newOutputStream(destPath)) {
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                }
            }

            dependencyUrls.add(destPath.toUri().toURL());
        }

        // 从Manifest获取真实业务主类
        Manifest manifest = new Manifest(DependencyLoader.class.getResourceAsStream("/META-INF/MANIFEST.MF"));
        String appMainClass = manifest.getMainAttributes().getValue("App-Main-Class");

        // 加载依赖并启动主程序
        URLClassLoader classLoader = new URLClassLoader(dependencyUrls.toArray(new URL[0]),
                DependencyLoader.class.getClassLoader());
        Class<?> mainClass = classLoader.loadClass(appMainClass);
        mainClass.getMethod("main", String[].class).invoke(null, (Object) args);
    }
}

3. 打包与运行

执行Gradle打包命令:

./gradlew jar

运行生成的可执行Jar:

java -jar build/libs/[你的项目名称].jar

首次运行会自动下载所有依赖到当前目录的lib文件夹,后续运行将直接复用本地已下载的依赖。


内容的提问来源于stack exchange,提问作者Chirag Suthar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 03:10:02