如何在 gradle 中导出可执行 jar,并且这个 jar 可以运行,因为它包含参考库

Posted

技术标签:

【中文标题】如何在 gradle 中导出可执行 jar,并且这个 jar 可以运行,因为它包含参考库【英文标题】:how to export a executable jar in gradle, and this jar can run as it include reference libraries 【发布时间】:2013-01-16 08:47:21 【问题描述】:

如何在 gradle 中导出一个可执行的 jar,这个 jar 可以运行,因为它包含了参考库。

build.gradle

apply plugin: 'java'

manifest.mainAttributes("Main-Class" : "com.botwave.analysis.LogAnalyzer")

repositories 
    mavenCentral()


dependencies 
    compile (
        'commons-codec:commons-codec:1.6',
        'commons-logging:commons-logging:1.1.1',
        'org.apache.httpcomponents:httpclient:4.2.1',
        'org.apache.httpcomponents:httpclient:4.2.1',
        'org.apache.httpcomponents:httpcore:4.2.1',
        'org.apache.httpcomponents:httpmime:4.2.1',
        'ch.qos.logback:logback-classic:1.0.6',
        'ch.qos.logback:logback-core:1.0.6',
        'org.slf4j:slf4j-api:1.6.0',
        'junit:junit:4.+'
    )

运行后:gradle build

它创建构建文件夹,我在 build/libs/XXX.jar 中运行 jar:

java -jar build/libs/XXX.jar

这里有一个执行说:

Exception in thread "main" java.lang.NoClassDefFoundError: ch/qos/logback/core/joran/spi/JoranException

如何使用参考库运行它?

【问题讨论】:

【参考方案1】:

你可以通过Gradle application plugin实现它

【讨论】:

酷,我不知道他们添加了这个。我一直使用“fat jar”配方,并为主类手动添加了一个清单条目。 他们在 1.0 版本左右添加了这一点 我不想获取 zip 或 tar 文件,只需要文件夹中的依赖库 JAR Gradle 应用程序插件不创建可执行 jar。 使用 Gradle 应用插件无法实现。【参考方案2】:

希望这对某人有所帮助(不幸的是,我花了很长时间试图找到解决方案)。这是对我有用的创建可执行 JAR 的解决方案。我将 Jetty 嵌入到主要方法中,具体来说是 Jetty 9 并使用 Gradle 2.1。

在您的 build.gradle 文件中包含以下代码(如果子项目是需要构建 jar 的“主”项目,则将其添加到应该像此项目一样开始的子项目中(':')在依赖项之后的某处插入代码。。

此外,您需要添加插件 java 才能工作:apply plugin: 'java' 。

我的 jar 任务如下所示:

apply plugin: 'java'

jar 

    archiveName = "yourjar.jar"

    from 

        configurations.runtime.collect 
            it.isDirectory() ? it : zipTree(it)
        

        configurations.compile.collect 
            it.isDirectory() ? it : zipTree(it)
        
    

    manifest 
        attributes 'Main-Class': 'your.package.name.Mainclassname'
    

    exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'

然后你可以通过命令行执行你的 yourjar.jar:

java -jar yourjar.jar

必须排除 META-INF/.RSA、META-INF/.SF 和 META-INF/*.DSA 才能正常工作。否则会抛出 SecurityException。

问题似乎出在嵌入式 Jetty 上,因为 Jetty 迁移到 Eclipse,现在正在签署他们的 JAR,当其他未签名的 JAR 想要加载已签名的 JAR 时,我读到这会成为问题。如果我在这方面错了,请随时教育我,这就是我读到的。

项目所依赖的JAR在dependencies中定义如下:

dependencies 

    // add the subprojects / modules that this depends on
    compile project(':subproject-1')
    compile project(':subproject-2')

    compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.2.6.v20141205'
    compile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.2.6.v20141205'
    compile group: 'org.eclipse.jetty', name: 'jetty-http', version: '9.2.6.v20141205'


编辑:之前,而不仅仅是

configurations.runtime.collect...

我有

configurations.runtime.asFileTree.files.collect...

这在干净构建的大型项目中引起了奇怪的行为。第一次执行 gradle clean build 后运行 jar (手动清理构建目录后),会抛出 NoClassDefFoundException (在我们有很多子项目的项目中),但在第二次执行 gradle clean build 后运行 jar(没有手动清空构建目录),由于某种原因它具有所有依赖项。如果 asFileTree.files 被忽略,这不会发生。

另外我应该注意,所有编译依赖项都包含在运行时中,但并非所有运行时都包含在编译中。所以如果你只是使用编译

        configurations.compile.collect 
            it.isDirectory() ? it : zipTree(it)
        

那么一定要记住,如果抛出了 NoClassDefFoundException,则在运行时找不到某些类,这意味着您还应该包含以下内容:

        configurations.runtime.collect 
            it.isDirectory() ? it : zipTree(it)
        

【讨论】:

我还应该注意,在我的代码中,依赖项 ... 部分位于 jar ... 部分之前,以防相关。 -1 OP 是明确的:他/她想要创建一个包含所有“参考库”的“胖罐”。当然,你不是唯一一个回答错误问题的人。看我的回答。 我看不到 OP 在哪里使用“胖罐子”这个词。 OP 清楚地谈到了一个可执行的 jar。我知道可执行 jar 基本上被称为 fat jar,但这是否立即意味着您必须使用 fat jar 插件?我还明确说明了我正在使用哪些版本,所以是的,这可能已经过时了,但它适用于我提到的 gradle 等版本。 OP 不知道“胖罐子”这个词,我同意!但是如何解释这个想法:“这个 jar 可以运行,因为它包含参考库。”?当然,“参考库”这个术语是指构成依赖项的 jars ......并且 OP 想知道她/他为什么收到错误消息......“fat jar”表示可执行的 jar,是的,但具体 一个包含所有依赖项的 jars...【参考方案3】:

快速解答

    将以下内容添加到您的build.gradle

    apply plugin: 'application'
    
    mainClassName = 'org.example.app.MainClass'
    
    jar 
        manifest 
            attributes 'Main-Class': mainClassName,
                       'Class-Path': configurations.runtime.files.collect "$it.name".join(' ')
        
    
    
    从项目目录,运行gradle installDist 运行java -jar build/install/<appname>/lib/<appname>.jar

我建议您也将应用版本添加到您的build.gradle,但这不是必需的。如果这样做,则构建的 jar 名称将为 <appname>-<version>.jar

注意:我使用的是 gradle 2.5


详情

为了创建一个可以简单运行的自包含可执行 jar:

java -jar appname.jar

你需要:

    您的 jar 文件包含指向您的应用程序主类的 MANIFEST 文件 您的所有依赖项(来自应用程序外部 jar 的类)都将被包含或以某种方式访问​​ 您的 MANIFEST 文件以包含正确的类路径

正如其他一些答案所指出的,您可以使用一些第三方插件来实现这一点,例如shadow 或one-jar。

我尝试了 shadow,但不喜欢 所有 我的依赖项及其资源与我的应用程序代码一起被完全倾倒到构建的 jar 中的事实。我也更喜欢尽量减少使用外部插件。

另一种选择是使用@erdi 上面回答的gradle application plugin。运行 gradle build 将为您构建一个 jar,并将它与您的所有依赖项很好地捆绑在一个 zip/tar 文件中。你也可以直接运行gradle installDist 跳过压缩。

然而,正如@jeremyjjbrown 在评论中所写的那样,插件本身创建一个可执行的 jar。它创建一个 jar 和一个构建类路径并执行命令以运行应用程序的主类的脚本。你将无法运行java -jar appname.jar

为了两全其美,请按照上述步骤将您的 jar 与所有依赖项一起创建为单独的 jar,并将正确的值添加到您的 MANIEST。

【讨论】:

谢谢,如果我想创建一个包含所有依赖项的 jar,那可以工作,但是关于依赖项? @max 抱歉,我没有完全理解您的问题。以上将为您的应用程序创建一个 jar,其中包含所有依赖项作为单独的 jar。 MANIFEST 类路径将指向您所依赖的所有 jar。 是的,这是正确的,但如果我希望我的所有依赖项和应用程序都放在一个 jar 中怎么办? @max 我认为您可以使用我在答案中链接到的 shadowone-jar 插件。【参考方案4】:

所有这些答案要么是错误的,要么是过时的。

OP 要求的是所谓的“胖罐”。这是一个包含所有依赖项的可执行 jar,因此它不需要外部依赖项即可运行(当然 JRE 除外!)。

撰写本文时的答案是 Gradle Shadow Jar 插件,在 Shadow Plugin User Guide & Examples 上解释得很清楚。

我有点挣扎。但这有效:

将所有这些行放在 build.gradle 文件中的某个位置(我将它们放在顶部附近):

buildscript 
    repositories 
        jcenter()
    
    dependencies 
        classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.4' 
    

apply plugin: 'com.github.johnrengelman.shadow'
shadowJar 
    baseName = 'shadow'
    classifier = null
    version = null

jar 
    manifest 
        attributes 'Class-Path': '/libs/a.jar'
        attributes 'Main-Class': 'core.MyClassContainingMainMethod'
    

PS 不要担心构建文件中其他地方的任何其他“存储库”、“依赖项”或“插件”行,并且将这些行留在这个“buildscript”块中(我不知道为什么需要这样做)。

PPS Shadow Plugin User Guide & Examples 写得很好,但没有告诉你 包括该行

attributes 'Main-Class': 'core.MyClassContainingMainMethod'

我把它放在上面的地方。也许是因为作者认为你没有我那么无知,而你可能是。我不知道为什么我们被告知要在其中放置一个奇怪的“类路径”属性,但如果它没有损坏,请不要修复它。

你去的时候

> gradle shadowjar

Gradle 有望在 /build/libs(默认名称“shadow.jar”)下构建一个胖可执行 jar,您可以通过以下方式运行它:

> java -jar shadow.jar

【讨论】:

@elanh 答案使用 gradle 开箱即用,不需要 shadowjar @marcoslhc 我认为您没有理解这个问题:OP 想要一个包含其所有 compile 依赖项 jar 的 jar。所以你可以把这个罐子放在任何地方,然后去java -jar myFatJar.jar ...它就会运行。 elanh 在对他的问题的评论中说了同样的话。 拯救我的一天!该解决方案适用于我 2018 年 2 月的项目。【参考方案5】:

我检查了很多解决方案的链接,最后完成了下面提到的步骤以使其正常工作。我正在使用 Gradle 2.9。

在您的 build、gradle 文件中进行以下更改:

1.提及插件:

apply plugin: 'eu.appsatori.fatjar'

2.提供 Buildscript:

buildscript 
  repositories 
    jcenter()
   

dependencies 
  classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
  

3.提供主类:

fatJar 
  classifier 'fat'
  manifest 
     attributes 'Main-Class': 'my.project.core.MyMainClass'
  
  exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'

4.创建 fatjar:

./gradlew clean fatjar

5.从 /build/libs/ 运行 fatjar:

java -jar MyFatJar.jar

【讨论】:

试过这个...找不到'eu.appsatori.fatjar'。这里的plugins.gradle.org/plugin/eu.appsatori.fatjar 指向这里的github.com/musketyr/gradle-fatjar-plugin,上面写着“不再积极开发,改用“Gradle Shadow Plugin”。但至少你回答了这个问题!

以上是关于如何在 gradle 中导出可执行 jar,并且这个 jar 可以运行,因为它包含参考库的主要内容,如果未能解决你的问题,请参考以下文章

我可以选择我在 Maven 依赖项中使用的唯一包来在 Eclipse 中导出可运行的 jar 吗?

从 Gradle 创建可执行 jar 文件并在 maven 中导入 jar

如何使用 Gradle 创建具有实现依赖项的可执行胖 jar

如何在 Eclipse IDE 中导出 Spring Boot Gradle 项目?

Spring Boot + Gradle:如何构建可执行jar

如何在我的gradle项目中创建单个可执行的jar?