【问题标题】:Spigot with Guice gives NoClassDefFoundError on internal.Messages$FormatOptionsSpigot 与 Guice 在 internal.Messages$FormatOptions 上给出 NoClassDefFoundError
【发布时间】:2021-11-24 23:33:08
【问题描述】:

我一直在玩 Guice,偶然发现了这个对我来说似乎没有多大意义的错误。似乎每当在注入过程中发生错误时,Guice 由于消息格式失败而无法正确记录注入错误。这可能是 Guice 中的错误,我不完全确定。

jar 正在使用maven-shade-plugin 构建,其中包括 jar 本身中的必要依赖项。查看 jar 的内容,它确实包含 NoClassDefFoundError 所指的类。注入本身完全可以正常工作,只要它没有遇到任何无法正确注入的类。

让这个奇怪的是,发生异常的代码与丢失的类位于完全相同的文件中。以下屏幕截图是使用 IntelliJ 的内置反编译器截取的:

com.google.inject.internal 通过 winrar 的 jar 内容:

堆栈跟踪:

[23:32:10 WARN]: AsyncLogger error handling event seq=231, value='org.apache.logging.log4j.core.async.RingBufferLogEvent@64c2c9e5': java.lang.NoClassDefFoundError: java.lang.NoClassDefFoundError: com/google/inject/internal/Messages$FormatOptions
[23:32:10 WARN]:        at Dodgeball.jar//com.google.inject.internal.Messages.redBold(Messages.java:306)
[23:32:10 WARN]:        at Dodgeball.jar//com.google.inject.spi.ErrorDetail.lambda$format$0(ErrorDetail.java:61)
[23:32:10 WARN]:        at java.base/java.util.Optional.map(Optional.java:260)
[23:32:10 WARN]:        at Dodgeball.jar//com.google.inject.spi.ErrorDetail.format(ErrorDetail.java:61)
[23:32:10 WARN]:        at Dodgeball.jar//com.google.inject.internal.Messages.formatMessages(Messages.java:90)
[23:32:10 WARN]:        at Dodgeball.jar//com.google.inject.ProvisionException.getMessage(ProvisionException.java:60)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.impl.ThrowableProxy.<init>(ThrowableProxy.java:106)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.impl.ThrowableProxy.<init>(ThrowableProxy.java:94)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.async.RingBufferLogEvent.getThrownProxy(RingBufferLogEvent.java:326)
[23:32:10 WARN]:        at io.papermc.paper.logging.DelegateLogEvent.getThrownProxy(DelegateLogEvent.java:103)
[23:32:10 WARN]:        at io.papermc.paper.logging.ExtraClassInfoLogEvent.getThrownProxy(ExtraClassInfoLogEvent.java:21)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter.format(ExtendedThrowablePatternConverter.java:63)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.pattern.PatternFormatter.format(PatternFormatter.java:38)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.layout.PatternLayout$PatternSelectorSerializer.toSerializable(PatternLayout.java:475)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:244)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:229)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:59)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender.append(RollingRandomAccessFileAppender.java:251)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.rewrite.RewriteAppender.append(RewriteAppender.java:84)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.appender.rewrite.RewriteAppender.append(RewriteAppender.java:84)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:540)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:498)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:481)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:469)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:98)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.async.AsyncLogger.actualAsyncLog(AsyncLogger.java:488)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.async.RingBufferLogEvent.execute(RingBufferLogEvent.java:154)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.async.RingBufferLogEventHandler.onEvent(RingBufferLogEventHandler.java:46)
[23:32:10 WARN]:        at org.apache.logging.log4j.core.async.RingBufferLogEventHandler.onEvent(RingBufferLogEventHandler.java:29)
[23:32:10 WARN]:        at com.lmax.disruptor.BatchEventProcessor.processEvents(BatchEventProcessor.java:168)
[23:32:10 WARN]:        at com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:125)
[23:32:10 WARN]:        at java.base/java.lang.Thread.run(Thread.java:831)

Main Pom.xml(注意 Valve 是包含 guice 的):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mpolder</groupId>
    <artifactId>Dodgeball</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>16</maven.compiler.source>
        <maven.compiler.target>16</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.3.0-SNAPSHOT</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <finalName>Cameo-Dodgeball</finalName>
                            <relocations>
                                <relocation>
                                    <pattern>com.mpolder.valve</pattern>
                                    <shadedPattern>com.mpolder.dodgeball.valve</shadedPattern>
                                </relocation>
                            </relocations>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>maven-snapshots</id>
            <url>https://repository.apache.org/content/repositories/snapshots/</url>
        </pluginRepository>
    </pluginRepositories>

    <repositories>
        <repository>
            <id>spigot-repo</id>
            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
        </repository>
        <repository>
            <id>sk89q-repo</id>
            <url>https://maven.enginehub.org/repo/</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>22.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <dependency>
            <groupId>org.spigotmc</groupId>
            <artifactId>spigot-api</artifactId>
            <version>1.16.5-R0.1-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.mpolder</groupId>
            <artifactId>Valve</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.sk89q.worldguard</groupId>
            <artifactId>worldguard-bukkit</artifactId>
            <version>7.0.7-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

阀门 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mpolder</groupId>
    <artifactId>Valve</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>16</maven.compiler.source>
        <maven.compiler.target>16</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>spigot-repo</id>
            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>22.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.9.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jdbi/jdbi3-core -->
        <dependency>
            <groupId>org.jdbi</groupId>
            <artifactId>jdbi3-core</artifactId>
            <version>3.23.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jdbi/jdbi3-core -->
        <dependency>
            <groupId>org.jdbi</groupId>
            <artifactId>jdbi3-sqlobject</artifactId>
            <version>3.23.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>5.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.spigotmc</groupId>
            <artifactId>spigot-api</artifactId>
            <version>1.16.5-R0.1-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

【问题讨论】:

  • 如果不包括您的代码(堆栈跟踪中的类/方法)和 pom.xml,任何人都无法说出问题所在
  • @morgwai 实际项目本身似乎大多不相关。我将添加我的 pom.xml,以便可以看到实际的 jar 阴影。除此之外,它只是使用了一个非常基本的 guice 注入,所以我不能真正分享任何其他内容
  • @morgwai 小澄清。在原始帖子中,我的异常源于简单地尝试注入一个没有绑定实现的接口。
  • 这个 pom 真的很有帮助。看起来确实很奇怪。因为正如你所指出的那样,类本身就在罐子里,所以这似乎不是一个 Guice 问题。我宁愿在着色期间出现一些不正确的重定位问题,这让我想到了这个问题:你为什么使用maven-shade-plugin 的快照版本?你似乎在做相当标准的事情,所以发布应该做,不是吗?我的直觉告诉我,这是整个问题的主要嫌疑人。
  • @morgwai 几个月后我想我已经弄明白了。在那段时间里,我向其他几个人寻求帮助,最后有人把我带到了某个地方。如果您仍然想知道,我已经更新了一个答案,但它并不漂亮:)

标签: java log4j classloader guice spigot


【解决方案1】:

所以,这可能不是任何人(包括我)所希望的解决方案

事实证明,我使用的名为 spigot(bukkit 的扩展)的库有自己的类加载器实现。这个类加载器似乎无法正常运行,并且无法加载子类即使该类位于类路径中

一旦找到问题的原因,我能想到的唯一方法就是使用 Google Guava 手动加载类。这已经解决了这个问题,尽管是以一种非常肮脏的方式。不过它只需要运行一次,所以我把它藏在了一个方法的深处,这个方法在这个 bug 上调用了 bukkit。

如果您想知道我是如何修复它的,请准备好疯狂的旅程:

    List<String> toLoad = Arrays.asList(
            "com.google.inject.internal.Messages$FormatOptions",
            "com.google.inject.internal.util.LineNumbers",
            "com.google.inject.internal.util.LineNumbers$LineNumberReader",
            "com.google.inject.internal.PackageNameCompressor",
            "com.google.inject.internal.ErrorFormatter"
    );

    try {
        Set<ClassPath.ClassInfo> cp = ClassPath.from(ValvePlugin.class.getClassLoader()).getAllClasses();
        for (ClassPath.ClassInfo x : cp) {
            if (toLoad.contains(x.getName())) {
                x.load();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

此代码手动扫描整个类路径中的所有类(我找不到一种只扫描一个包同时还包括子类的快速方法)并加载列表中存在的任何名称。

【讨论】:

  • 现在这是一个非常糟糕的错误!我讨厌 Java 中的 ClassLoader 机制,顺便说一句:每当有人实现他自己的机制时,一只小猫就会死去...... ;-)
  • 我已经更新了标题和标签,以便其他人更容易找到:我敢打赌,有些人已经因为这个错误而失去了一些头发;-]
猜你喜欢
  • 2018-08-14
  • 1970-01-01
  • 1970-01-01
  • 2019-04-16
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
  • 2012-04-17
  • 2021-10-27
相关资源
最近更新 更多