简体   繁体   English

"使用 Gradle 创建可运行的 JAR"

[英]Creating runnable JAR with Gradle

Until now I created runnable JAR files via the Eclipse "Export..." functionallity but now I switched to IntelliJ IDEA and Gradle for build automation.到目前为止,我通过 Eclipse“导出...”功能创建了可运行的 JAR 文件,但现在我切换到 IntelliJ IDEA 和 Gradle 以实现构建自动化。

Some articles here suggest the "application" plugin, but this does not entirely lead to the result I expected (just a JAR, no start scripts or anything like this).这里的一些文章建议使用“应用程序”插件,但这并不完全导致我预期的结果(只是一个 JAR,没有启动脚本或类似的东西)。

How can I achieve the same result Eclipse does with the "Export..." dialog?如何使用“导出...”对话框获得与 Eclipse 相同的结果?

An executable jar file is just a jar file containing a Main-Class entry in its manifest.一个可执行的 jar 文件只是一个在其清单中包含一个 Main-Class 条目的 jar 文件。 So you just need to configure the jar task in order to add this entry in its manifest:因此,您只需要配置jar 任务即可在其清单中添加此条目:

jar {
    manifest {
        attributes 'Main-Class': 'com.foo.bar.MainClass'
    }
}

You might also need to add classpath entries in the manifest, but that would be done the same way.您可能还需要在清单中添加类路径条目,但这将以相同的方式完成。

See http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html请参阅http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

Both JB Nizet and Jorge_B's answers are correct. JB Nizet 和 Jorge_B 的答案都是正确的。

In its simplest form, creating an executable JAR with Gradle is just a matter of adding the appropriate entries to the manifest .在最简单的形式中,使用 Gradle 创建可执行 JAR 只是将适当的条目添加到manifest 的问题 However, it's much more common to have dependencies that need to be included on the classpath, making this approach tricky in practice.但是,需要将依赖项包含在类路径中的情况更为常见,这使得这种方法在实践中变得棘手。

The application plugin provides an alternate approach; 应用程序插件提供了另一种方法; instead of creating an executable JAR, it provides:它没有创建可执行的 JAR,而是提供:

  • a run task to facilitate easily running the application directly from the build一个run任务,以方便直接从构建中轻松运行应用程序
  • an installDist task that generates a directory structure including the built JAR, all of the JARs that it depends on, and a startup script that pulls it all together into a program you can run一个installDist任务,它生成一个目录结构,包括构建的 JAR、它依赖的所有 JAR,以及一个启动脚本,该脚本将所有这些组合到一个您可以运行的程序中
  • distZip and distTar tasks that create archives containing a complete application distribution (startup scripts and JARs) distZipdistTar任务创建包含完整应用程序分发(启动脚本和 JAR)的档案

A third approach is to create a so-called "fat JAR" which is an executable JAR that includes not only your component's code, but also all of its dependencies.第三种方法是创建一个所谓的“胖 JAR”,它是一个可执行的 JAR,不仅包含组件的代码,还包含其所有依赖项。 There are a few different plugins that use this approach.有几个不同的插件使用这种方法。 I've included links to a few that I'm aware of;我已经包含了一些我知道的链接; I'm sure there are more.我确定还有更多。

As others have noted, in order for a jar file to be executable, the application's entry point must be set in the Main-Class attribute of the manifest file.正如其他人所指出的,为了使 jar 文件可执行,必须在清单文件的Main-Class属性中设置应用程序的入口点。 If the dependency class files are not collocated, then they need to be set in the Class-Path entry of the manifest file.如果依赖类文件没有并置,则需要在清单文件的Class-Path条目中设置它们。

I have tried all kinds of plugin combinations and what not for the simple task of creating an executable jar and somehow someway, include the dependencies.我已经尝试了各种插件组合,而不是为了创建可执行 jar 的简单任务,并以某种方式包含依赖项。 All plugins seem to be lacking one way or another, but finally I got it like I wanted.所有插件似乎都缺乏一种或另一种方式,但最终我得到了我想要的。 No mysterious scripts, not a million different mini files polluting the build directory, a pretty clean build script file, and above all: not a million foreign third party class files merged into my jar archive.没有神秘的脚本,没有一百万个不同的小文件污染构建目录,一个非常干净的构建脚本文件,最重要的是:没有一百万个外国第三方类文件合并到我的 jar 档案中。

The following is a copy-paste from here for your convenience..为了您的方便,以下是从这里复制粘贴..

[How-to] create a distribution zip file with dependency jars in subdirectory /lib and add all dependencies to Class-Path entry in the manifest file: [How-to] 在子目录/lib创建一个带有依赖项 jar 的分发 zip 文件,并将所有依赖项添加到清单文件中的Class-Path条目:

apply plugin: 'java'
apply plugin: 'java-library-distribution'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-lang3:3.3.2'
}

// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)

jar {
    // Keep jar clean:
    exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'

    manifest {
        attributes 'Main-Class': 'com.somepackage.MainClass',
                   'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
    }
    // How-to add class path:
    //     https://stackoverflow.com/questions/22659463/add-classpath-in-manifest-using-gradle
    //     https://gist.github.com/simon04/6865179
}

Hosted as a gist here . 在这里作为要点托管。

The result can be found in build/distributions and the unzipped contents look like this:结果可以在build/distributions找到,解压后的内容如下所示:

lib/commons-lang3-3.3.2.jar lib/commons-lang3-3.3.2.jar
MyJarFile.jar MyJarFile.jar

Contents of MyJarFile.jar#META-INF/MANIFEST.mf : MyJarFile.jar#META-INF/MANIFEST.mf

Manifest-Version: 1.0清单版本:1.0
Main-Class: com.somepackage.MainClass主类:com.somepackage.MainClass
Class-Path: lib/commons-lang3-3.3.2.jar类路径:lib/commons-lang3-3.3.2.jar

Least effort solution for me was to make use of the gradle-shadow-plugin对我来说最省力的解决方案是使用gradle-shadow-plugin

Besides applying the plugin all that needs to be done is:除了应用插件,所有需要做的是:

Configure the jar task to put your Main class into manifest配置 jar 任务以将您的 Main 类放入清单

jar {
  manifest {
   attributes 'Main-Class': 'com.my.app.Main'
  }
}

Run the gradle task运行gradle任务

./gradlew shadowJar

Take the app-version-all.jar from build/libs/build/libs/获取app-version-all.jar

And finally execute it via:最后通过以下方式执行它:

java -jar app-version-all.jar

Have you tried the 'installApp' task?您是否尝试过“installApp”任务? Does it not create a full directory with a set of start scripts?它不会创建一个包含一组启动脚本的完整目录吗?

http://www.gradle.org/docs/current/userguide/application_plugin.html http://www.gradle.org/docs/current/userguide/application_plugin.html

You can use the SpringBoot plugin:您可以使用 SpringBoot 插件:

plugins {
  id "org.springframework.boot" version "2.2.2.RELEASE"
}

Create the jar创建罐子

gradle assemble

And then run it然后运行它

java -jar build/libs/*.jar

Note: your project does NOT need to be a SpringBoot project to use this plugin.注意:您的项目不需要是 SpringBoot 项目即可使用此插件。

Thank you Konstantin, it worked like a charm with few nuances.谢谢康斯坦丁,它就像一种魅力,几乎没有细微差别。 For some reason, specifying main class as part of jar manifest did not quite work and it wanted the mainClassName attribute instead.出于某种原因,将主类指定为 jar 清单的一部分并不完全有效,而是需要 mainClassName 属性。 Here is a snippet from build.gradle that includes everything to make it work:这是 build.gradle 中的一个片段,其中包含使其工作的所有内容:

plugins {
  id 'java' 
  id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
    baseName = 'myapp'
}

After running gradle shadowJar you get myapp-{version}-all.jar in your build folder which can be run as java -jar myapp-{version}-all.jar.运行 gradle shadowJar 后,您将在构建文件夹中获得 myapp-{version}-all.jar,该文件夹可以作为 java -jar myapp-{version}-all.jar 运行。

You can define a jar artifact in the module settings (or project structure).您可以在模块设置(或项目结构)中定义一个 jar 工件。

  • Right click the module > Open module settings > Artifacts > + > JAR > from modules with dependencies.右键单击模块 > 打开模块设置 > 工件 > + > JAR > 来自具有依赖项的模块。
  • Set the main class.设置主类。

Making a jar is then as easy as clicking "Build artifact..." from the Build menu.制作 jar 就像从 Build 菜单中单击“Build artifact...”一样简单。 As a bonus, you can package all the dependencies into a single jar.作为奖励,您可以将所有依赖项打包到一个 jar 中。

Tested on IntelliJ IDEA 14 Ultimate.在 IntelliJ IDEA 14 Ultimate 上测试。

I checked quite some links for the solution, finally did the below mentioned steps to get it working.我检查了很多解决方案的链接,最后执行了下面提到的步骤以使其正常工作。 I am using Gradle 2.9.我正在使用 Gradle 2.9。

Make the following changes in your build,gradle file :在您的 build,gradle 文件中进行以下更改:

  1. Mention plugin:提及插件:

     apply plugin: 'eu.appsatori.fatjar'
  2. Provide the Buildscript:提供构建脚本:

     buildscript { repositories { jcenter() } dependencies { classpath "eu.appsatori:gradle-fatjar-plugin:0.3" } }
  3. Provide the Main Class:提供主类:

     fatJar { classifier 'fat' manifest { attributes 'Main-Class': 'my.project.core.MyMainClass' } exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF' }
  4. Create the fatjar:创建胖子:

     ./gradlew clean fatjar
  5. Run the fatjar from /build/libs/ :从 /build/libs/ 运行 fatjar:

     java -jar MyFatJar.jar

Here is the solution I tried with Gradle 6.7这是我在 Gradle 6.7 上尝试的解决方案

Runnable fat Jar (with all dependent libraries copied to the jar) Runnable fat Jar(将所有依赖库复制到 jar 中)

task fatJar(type: Jar) {
   
    manifest {
        attributes 'Main-Class': 'com.example.gradle.App'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    } with jar
    
}

Runnable jar with all dependencies copied to a directory and adding the classpath to the manifest将所有依赖项复制到目录并将类路径添加到清单的可运行 jar

    def dependsDir = "${buildDir}/libs/dependencies/"
    task copyDependencies(type: Copy) {
        from configurations.compile
        into "${dependsDir}"
    }
    task createJar(dependsOn: copyDependencies, type: Jar) {
      
        manifest {
            attributes('Main-Class': 'com.example.gradle.App',
                    'Class-Path': configurations.compile.collect { 'dependencies/' + it.getName() }.join(' ')
            )
        }
        with jar
    }

How to use ?如何使用 ?

  • Add the above tasks to build.gradle将上述任务添加到 build.gradle
  • Execute gradle fatJar //create fatJar执行gradle fatJar //创建 fatJar
  • Execute gradle createJar // create jar with dependencies copied.执行gradle createJar // 创建 jar 并复制依赖项。

More details : https://jafarmlp.medium.com/a-simple-java-project-with-gradle-2c323ae0e43d更多细节: https : //jafarmlp.medium.com/a-simple-java-project-with-gradle-2c323ae0e43d

  1. Configure Main Class to your Manifest将主类配置到您的清单

If you are using gradle project, just add the following into your build.gradle如果您使用的是 gradle 项目,只需将以下内容添加到您的 build.gradle 中

jar {
    manifest {
        attributes(
               'Main-Class': 'pokerhandscorer.PokerHandScorer'
        )
    }
}
  • Where 'pokerhandscorer' is the name of the package name, and PokerHandScorer is the main class name其中'pokerhandscorer'是包名,PokerHandScorer是主类名

This creates a jar file into your \\build\\libs{jarFilename}.jar这会在您的 \\build\\libs{jarFilename}.jar 中创建一个 jar 文件

  1. Run jar file using java -jar /{path}/{jarFileName.jar}使用 java -jar /{path}/{jarFileName.jar} 运行 jar 文件

java -jar /{path}/{jarFileName.jar} java -jar /{path}/{jarFileName.jar}

This is for Kotlin DSL (build.gradle.kts).这适用于 Kotlin DSL (build.gradle.kts)。

Method 1 (no need for application or other plugins)方法1(不需要application或其他插件)

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    // OR another notation
    // manifest {
    //     attributes["Main-Class"] = "com.example.MyMainClass"
    // }
}

If you are using any external libraries, use below code.如果您使用任何外部库,请使用以下代码。 Put library JARs in libs sub-directory of where you put your result jar.将库 JAR 放在放置结果 jar 的libs子目录中。 Make sure your library JAR files do not contain space in their file name.确保您的库 JAR 文件的文件名中不包含空格。

tasks.jar {
    manifest.attributes["Main-Class"] = "com.example.MyMainClass"
    manifest.attributes["Class-Path"] = configurations
        .runtimeClasspath
        .get()
        .joinToString(separator = " ") { file ->
            "libs/${file.name}"
        }
}

Note that Java requires us to use relative URLs for the Class-Path attribute.请注意,Java 要求我们为Class-Path属性使用相对 URL。 So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems).因此,我们不能使用 Gradle 依赖项的绝对路径(这也容易被更改并且在其他系统上不可用)。 If you want to use absolute paths, maybe this workaround will work.如果您想使用绝对路径,也许这种解决方法会起作用。

Create the JAR with the following command:使用以下命令创建 JAR:

./gradlew jar

The result JAR will be created in build/libs/ directory by default.结果 JAR 将默认创建在build/libs/目录中。

Method 2: Using the Shadow plugin .方法二:使用影子插件

plugins {
    id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
    manifest.attributes["Main-Class"] = "org.example.MainKt"
}

Create the JAR with this command:使用以下命令创建 JAR:

./gradlew shadowJar

See Shadow documentations for more information about configuring the plugin.有关配置插件的更多信息,请参阅Shadow 文档

Running the created JAR运行创建的 JAR

java -jar my-artifact.jar

The above solutions were tested with:上述解决方案经过以下测试:

  • Java 17爪哇 17
  • Gradle 7.1 (which uses Kotlin 1.4.31 for .kts scripts) Gradle 7.1(将 Kotlin 1.4.31 用于.kts脚本)

For more information, see Oracle Java Documentation: Working with Manifest files .有关更多信息,请参阅Oracle Java 文档:使用清单文件

Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file).请注意,您的资源文件将自动包含在 JAR 文件中(假设它们被放置在/src/main/resources/目录或在构建文件中设置为资源根目录的任何自定义目录中)。 To access a resource file in your application, use this code:要访问应用程序中的资源文件,请使用以下代码:

  • Kotlin科特林
    val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText() // OR // val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
  • Java爪哇
    var stream = MyClass.class.getResourceAsStream("vegetables.txt");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM