![](/img/trans.png)
[英]NoClassDefFoundError at Runtime on class inside external .AAR library
[英]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 中依赖它。
我不太确定这里发生了什么。
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.....
}
}
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
的部分是正确的 - 这只是不好的做法。 相反,您应该删除,使消费项目其添加为显式依赖,或更改配置implementation
到runtimeOnly
。 这两种方法都很好,这取决于您希望将 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.