Java код генерирует NoClassDefFoundError для класса во внешней библиотеке - PullRequest
1 голос
/ 06 февраля 2020

У меня есть проект maven, в который я включил флягу, которую я создал, используя gradle в качестве зависимости pom. В том числе код Jar, я ссылаюсь на log4j logmanager. Когда я пытаюсь получить доступ к методу во внешнем банке, он выдает java .lang.NoClassDefFoundError в logmanager, на который ссылается класс во внешнем фляге.

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

Я инсталлирую этот флягу локально в .m2, используя mvn install: install-file, а затем получаю зависимость от него в 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";
    }
}

Ответы [ 3 ]

1 голос
/ 07 февраля 2020

Когда вы используете mvn install:install-file, вы устанавливаете файл jar и создаете pom по умолчанию для него, используя обычный Maven. Если вы ничего не сделаете, pom не будет содержать транзитивных зависимостей. В конце концов, Maven просто видит jar-файл и ничего не знает об окружающих скриптах Gradle. Вот почему он терпит неудачу во время выполнения, так как в библиотеке («внешней») отсутствуют зависимости Log4J.

Вместо этого вам следует использовать плагин Maven Publi sh для Gradle , чтобы создать правильный pom для вашей библиотеки. Сделайте это, добавив:

plugins {
    id 'maven-publish'
}

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

Затем вы можете загрузить jar-файл в локальный репозиторий .m2 с полным pom, используя gradle publishToMavenLocal.

Также, ответ Ысахно правильно в части о том, что не нужно log4j-core на вашем пути к классам компиляции - это просто плохая практика. Вместо этого вы должны либо удалить его и заставить проект-потребитель добавить его в качестве явной зависимости, или изменить конфигурацию с implementation на runtimeOnly. Оба подхода хороши, в зависимости от того, насколько тесно вы хотите соединить Log4j с вашей библиотекой.

Я также думаю, что это прекрасно, если вы используете API Log4J2 в библиотеке, даже если он может использоваться в проектах, использующих много различные реализации регистрации. В конце концов, привязать API Log4J2 к SLF4J так же легко, как и наоборот. И оба являются популярными и очень хорошим выбором.

0 голосов
/ 07 февраля 2020

В общем, причина, по которой библиотеки журналов имеют 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, объясняющий разницу между этими двумя .

В качестве примечания я бы предложил сделать вашу библиотеку зависимой не от API Log4j, а от Simple Logging Фасад для Java один, потому что тогда приложение-потребитель может выбирать, какую из реализаций библиотек журналов использовать. Как указано в FAQ по SLF4J :

[...] библиотеки и другие встроенные компоненты должны учитывать SLF4J для своих потребностей ведения журналов, поскольку библиотеки не могут позволить себе навязывать свой выбор среды ведения журналов конечный пользователь.

Примечание: Что бы вы ни выбрали, не используйте compile конфигурацию зависимостей в скрипте сборки Gradle, потому что она устарела некоторое время go и может не работать с будущими версиями Gradle. Конфигурация, приблизительно эквивалентная compile, равна api.

0 голосов
/ 06 февраля 2020

Возможно, вы захотите добавить и это compile group: 'org.apache.logging.log4j', name: 'log4j-1.2-api', version: '2.2' . Идея в том, что у вас просто недостаточно зависимостей, чтобы удовлетворить ваши потребности. Это часто случается с такими вещами, как логгеры

См .: неожиданное исключение: java .lang.NoClassDefFoundError: org / apache / log4j / LogManager

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...