繁体   English   中英

Java 代码为外部库中的类抛出 NoClassDefFoundError

[英]Java code throws NoClassDefFoundError for a class inside external library

我有一个 maven 项目,其中包含了一个使用 gradle 作为 pom 依赖项创建的 jar。 在包含 Jar 的代码中,我引用了 log4j logmanager。 当我尝试访问外部 jar 中的方法时,它会在外部 jar 中的类所指的日志管理器上抛出 java.lang.NoClassDefFoundError 。

exernal jar 的 build.gradle 是:

plugins {
    id 'java'
}

group 'com.somecompany.somethingelse'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {

    implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.0'
    implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.0'

}

我使用 gradle clean assemble 构建罐子

我使用 mvn install:install-file 在本地将这个 jar 安装到 .m2 中,然后在消费应用程序的 pom 中依赖它。

我不太确定这里发生了什么。

外部 Jar 类代码

package com.company.something;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public abstract class MyClass{

     private static Logger logger = LogManager.getLogger();

     public static String myMethod(String someInput){
         logger.info("entered myMethod");
         ......some code goes here.....

     }

}

Jar 消费类代码

import com.company.something.MyClass;

public class consumingClass{

   public String consumingMethod(){
      MyClass.myMethod("someinput");
      return "something";
    }
}

当您使用mvn install:install-file ,您正在安装 jar 文件并使用普通 Maven 为其创建默认 pom。 如果您不做任何其他事情,pom 将不包含任何传递依赖项。 毕竟,Maven 只是看到一个 jar 文件,对周围的 Gradle 脚本一无所知。 这就是它在运行时失败的原因,因为库(“外部”)pom 中缺少 Log4J 依赖项。

你应该做的是使用Maven Publish Plugin for Gradle为你的库创建一个合适的 pom。 通过添加以下内容来执行此操作:

plugins {
    id 'maven-publish'
}

publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
        }
    }
}

然后,您可以使用gradle publishToMavenLocal使用完整的 pom 将 jar 文件上传到本地 .m2 存储库。

此外, ysakhno 的回答在编译类路径中不需要log4j-core的部分是正确的 - 这只是不好的做法。 相反,您应该删除,使消费项目其添加为显式依赖,更改配置implementationruntimeOnly 这两种方法都很好,这取决于您希望将 Log4j 与您的库耦合的紧密程度。

我还认为在库中使用 Log4J2 API 非常好,即使它可以在使用许多不同日志实现的项目中使用。 毕竟,将 Log4J2 API 绑定到 SLF4J 就像反过来一样容易。 两者都是受欢迎且非常好的选择。

你可能想添加这个compile group: 'org.apache.logging.log4j', name: 'log4j-1.2-api', version: '2.2'想法是你没有足够的依赖来满足你的需求. 它经常发生在记录器之类的事情上

参考: 意外异常:java.lang.NoClassDefFoundError:org/apache/log4j/LogManager

一般来说,日志库有 2 个 JAR 的原因(如您提供的示例中)是为了让库仅针对库的 API JAR 进行编译,然后在运行时与实际实现一起工作(执行)日志库(在您的情况下是log4j-core )存在于消费应用程序的类路径上的某处。

考虑到上述情况,您必须分离库和应用程序之间的依赖关系,即在库的build.gradle您应该具有以下内容:

dependencies {

    implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.0'
    // Note: you do not need the 'actual' implementation of Log4j in your library
    // at all!  It should compile very well with just the API, you'll then have
    // to put an 'implementation' dependency on log4j-core in your consuming
    // application's build.gradle (or pom.xml for that matter)

}

然后在你的应用程序的 pom.xml 中,你必须把这个:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.13.0</version>
    </dependency>

如果您确实希望保持原样(强烈推荐)并将库中的依赖项包含在消费应用程序中,则使用api依赖项配置而不是implementation配置。 这是一个很好的StackOverflow 答案,解释了两者之间的区别

附带说明一下,我建议让您的库不依赖于 Log4j 的 API,而是依赖于 Java 的 Simple Logging Facade ,因为这样消费应用程序可以选择使用哪个日志库实现。 SLF4J 的常见问题解答中所述

[...] 库和其他嵌入式组件应该考虑 SLF4J 来满足他们的日志记录需求,因为库无法将他们选择的日志框架强加给最终用户。

注意:无论您选择做什么,都不要在 Gradle 构建脚本中使用compile依赖项配置,因为它已被弃用一段时间,并且可能不适用于未来版本的 Gradle。 大致相当于compile的配置是api

暂无
暂无

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

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