包括使用 Gradle 构建的 Java 库会抛出 NoClassDefFoundError

Including Java library built with Gradle throws NoClassDefFoundError

我正在编写一个 Java 库,我想用 Gradle 构建该库,然后从本地测试项目对其进行测试。

我更愿意为我的 objective 使用 Gradle 3.3。 该库应该为 Java5 及更高版本构建。

到目前为止,我的 build.gradle 看起来像这样:

plugins {
  id 'jvm-component'
  id 'java-lang'
}

repositories {
  mavenCentral()
}

model {
  components {
    main(JvmLibrarySpec) {
      sources {
        java {
          dependencies {
            module 'commons-codec:commons-codec:1.10'
            module 'org.apache.httpcomponents:httpcore:4.4.6'
            module 'org.apache.httpcomponents:httpclient:4.5.3'
          }
        }
      }

      api {
        exports 'io.simplepush'
      }

      targetPlatform 'java5'
    }
  }
}

库的源代码位于 src/main/java/io/simplepush/Notification.java 并依赖于 build.gradle 文件中所述的依赖项。

使用 ./gradlew build 构建库工作正常并生成 build/jars/main/jar/main.jar

然而,当我 运行 来自 IntelliJ 的测试项目(在包含 main.jar 到测试项目之后)时,我得到以下 运行 时间错误:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/HttpEntity.

测试项目似乎不知道我的库需要的运行时间依赖性。

我不确定告诉测试项目关于我的库的依赖项的正确方法是什么。 我不想要一个包含所有依赖项的胖罐子。
列出测试项目本身的所有依赖项也不是一个选项。
最好我希望库本身告诉测试项目它需要哪些依赖项。

您创建的库 jar 不包含任何依赖信息,IDE/Gradle 然后可以解析这些信息以便能够 compile/run 测试项目。我看到您正在使用 maven 中央存储库,所以您需要做的是将您的库发布到本地 maven 存储库,并在测试项目中添加依赖信息(不仅仅是普通的 jar 文件)。

所以在库和测试项目中 build.gradle 添加一个 maven 本地存储库配置。

repositories {
   mavenLocal()
   mavenCentral()
}

现在您需要将库发布到本地存储库。当您使用 gradle 3.3 时,您可以使用 Maven Publishing.

于是在库中build.gradle添加一个maven发布信息

publishing {
    publications {
        maven(MavenPublication) {
            groupId 'io.simplepush'
            artifactId 'project1-sample'
            version '1.1'

            from components.java
        }
    }
}

Gradle “maven-publish” 插件可以轻松发布到本地存储库,自动创建 PublishToMavenLocal 任务。 所以你可以 运行

gradle publishToMavenLocal

这会将您的库和所有依赖信息发布到本地 maven 存储库中。

然后你只需要在你的测试项目中添加一个库信息即可build.gradle

dependencies {
        // other dependencies .....
        module 'io.simplepush:project1-sample:1.1'
}

这指定要检查哪些存储库以从中获取依赖项

repositories {
  mavenCentral()
}

因此,dependecies{} 中的任何内容都将从上面的内容中获取。

如果测试项目未与库项目耦合,(@RaGe 示例)新测试项目需要知道从何处获取依赖项 - 您需要使用首选方法发布它。

之后,您的新测试项目需要在 build.gradle dependencies{}

中指定具有首选配置(编译...运行时等)的库

之后根据 IDE 您需要刷新类路径并从指定的之前存储库下载依赖项,库依赖项中指定的传递依赖项(在本例中)将从测试项目中获取 repositories{}


图书馆build.gradle

repositories {
  mavenCentral()
}

dependencies {
  module 'commons-codec:commons-codec:1.10'
  module 'org.apache.httpcomponents:httpcore:4.4.6'
  module 'org.apache.httpcomponents:httpclient:4.5.3'
}

测试项目build.gradle

repositories {
  mavenCentral() repository to fetch transitives
  mavenLocal() or any other repo that you published the library to
}

dependencies {
  pref-conf librarygroup:name:version
}

您可以在 gradle 中为 gradle ideagradle eclipseClasspath 任务使用 idea 或 eclipse 插件,以使用新添加的依赖项刷新它。

使用此解决方案,您应该不需要在库中打包传递依赖项,

PS。在你说你想要可执行的 jar 之后,我只是感到困惑。

我通过改变几件事解决了它。
感谢 @Babl 为我指明了正确的方向。
我的新图书馆 build.gradle 看起来像这样:

plugins {
  id 'java'
  id 'maven-publish'
}

sourceCompatibility = 1.5

repositories {
  mavenLocal()
  mavenCentral()
}

dependencies {
  compile 'commons-codec:commons-codec:1.10'
  compile 'org.apache.httpcomponents:httpcore:4.4.6'
  compile 'org.apache.httpcomponents:httpclient:4.5.3'
}

publishing {
  publications {
    maven(MavenPublication) {
      groupId 'io.simplepush'
      artifactId 'project1-sample'
      version '1.1'

      from components.java
    }
  }
}

现在我可以使用 ./gradlew publishToMavenLocal 将库推送到本地 maven 存储库。

测试项目的build.gradle使用了application插件,定义了一个mainclass(在我的例子中是Hello)。然后我可以 运行 ./gradlew installDist 生成一个可执行文件(参见 Application plugin docs),它将所有依赖项放在 class 路径和 运行 中就好了。

group 'com.test'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'application'

repositories {
  mavenLocal()
  mavenCentral()
}

dependencies {
  compile 'io.simplepush:project1-sample:1.1'
}

mainClassName = "Hello"