【问题标题】:If a class dependency is missing, how can I determine which class is missing?如果缺少类依赖项,我如何确定缺少哪个类?
【发布时间】:2017-12-07 22:57:16
【问题描述】:

我收到错误“无法找到或加载主类”,即使该类存在。从this answer 了解到,由于缺少依赖项,该类可能会被找到,但无法加载。

除了手动反编译类,检查它的所有依赖关系,以及它们是否在类路径上,等等,对于每个依赖的类,无限……有没有办法确定类路径中缺少哪个类因此导致 Java 无法加载我的主类?

另一个问题是缺少父/接口类的问题。但是我已经手动检查了所有的祖先类要么在指定的类路径上,要么在 JDK 中,如下所示。

$ cd ~/picketbox
$ java -cp picketbox-4.1.1.Final-redhat-1.jar org.picketbox.datasource.security.SecureIdentityLoginModule HelloWorld
Error: Could not find or load main class org.picketbox.datasource.security.SecureIdentityLoginModule
$ jar xvf picketbox-4.1.1.Final-redhat-1.jar > jarxvf.txt
$ cat jarxvf.txt | grep SecureIdentityLoginModule
 inflated: org/picketbox/datasource/security/SecureIdentityLoginModule.class
$ cd org/picketbox/datasource/security/
$ javap SecureIdentityLoginModule.class | grep main
  public static void main(java.lang.String[]) throws java.lang.Exception;
$ javap SecureIdentityLoginModule.class | grep extends
public class org.picketbox.datasource.security.SecureIdentityLoginModule extends org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractPasswordCredentialLoginModule
 inflated: org/picketbox/datasource/security/AbstractPasswordCredentialLoginModule.class
$ javap AbstractPasswordCredentialLoginModule.class | grep extends
public abstract class org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule extends org.jboss.security.auth.spi.AbstractServerLoginModule {
$ cat ~/picketbox/jarxvf.txt | grep AbstractServerLoginModule
 inflated: org/jboss/security/auth/spi/AbstractServerLoginModule.class
$ cd ~/picketbox/org/jboss/security/auth/spi/
$ javap AbstractServerLoginModule.class | grep implements
public abstract class org.jboss.security.auth.spi.AbstractServerLoginModule implements javax.security.auth.spi.LoginModule {
$ cd ~/rtjar
$ cp /usr/java/jdk1.8.0_141/jre/lib/rt.jar ./
$ jar xvf rt.jar | grep spi/LoginModule
extracted: javax/security/auth/spi/LoginModule.class
$ cd javax/security/auth/spi/
$ javap LoginModule.class | grep interface
public interface javax.security.auth.spi.LoginModule {
$

【问题讨论】:

  • 您是否尝试使用 -XX:+TraceClassLoading 选项?
  • @ClaudioCorsi 我以前不知道这个选项。我刚刚尝试过,但不幸的是它根本没有帮助。它只是显示 Java 从 rt.jar 加载了许多 JDK 类,并且仍然给出相同的错误消息。我试图将输出作为编辑附加到我的问题中,但不幸的是它导致我的问题超出了字符数限制。但是那里没有什么有用的东西,只是一堆[Loaded x.y.z.ClassName from /.../rt.jar]形式的行
  • 另一个选项是您可以实现一个 jvmti 模块并处理异常抛出的事件。然后您可以调用异常的 printStackTrace 方法。这应该有望提供有关缺少哪个类的线索。
  • @ClaudioCorsi 抱歉,我不知道该怎么做;我什至从未听说过jvmti。您可以发表您的评论作为答案并详细说明吗?

标签: java command-line-interface classloader


【解决方案1】:

创建一个辅助类:

public class Helper {
    public static void main(String[] args) {
        YourActualMainClass.main(args);
    }
}

并尝试运行此帮助程序类而不是 YourActualMainClass

关键是Helper的继承树及其成员的签名都不依赖于有问题的类,所以加载甚至初始化都会成功,考虑到HotSpot的惰性解析策略,所以它只会尝试加载和在尝试执行 Helper.main 方法时解析 YourActualMainClass。此时,它会抛出一个详细的错误,告诉您实际上缺少哪个类。

这与链接答案中描述的行为相匹配,即当使用类的继承不依赖于它时,类的 use 会导致特定的错误消息。


或者,您可以尝试使用 Java 9 运行应用程序,因为它的启动器会在主类加载失败时打印原因。

【讨论】:

  • 不幸的是,这没有帮助(没有双关语的意思)。它只是说“找不到或加载主类助手”。但是我后来发现 JAR 本身可能以某种方式损坏(不确定如何仍然可以提取它)。如果我提取 JAR 然后重新压缩它(即jar xvfjar cvf),它可以工作。此外,我必须重新构建才能编译 Helper.java,否则 javac 会报告“打开 zip 文件时出错”
【解决方案2】:

您可以尝试使用 JVMTI 来显示所有抛出的异常。以Identifying exceptions through JVMTI 开头。只需使用以下更新的 agent.c 源代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <jni.h>
#include <jvmti.h>

#define CHECK_JVMTI_ERROR(x,call) \
    { if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }

/* Global static data */
static jvmtiEnv     *jvmti;

static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
    jthread thread, jmethodID method, jlocation location, jobject exception,
    jmethodID catch_method, jlocation catch_location)
{
    jclass exceptionClass = (*jni_env)->GetObjectClass(jni_env, exception);

    jmethodID methodId = (*jni_env)->GetMethodID(jni_env, exceptionClass,
                         "printStackTrace",
                         "()V");

    (*jni_env)->CallVoidMethod(jni_env, exception, methodId);
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
    jint                rc;
    jvmtiError          r;
    jvmtiCapabilities   capabilities;
    jvmtiEventCallbacks callbacks;

    /* Get JVMTI environment */
    rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
    if (rc != JNI_OK)
    {
        fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
        return -1;
    }

    /* Get/Add JVMTI capabilities */
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_generate_exception_events = 1;
    r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    CHECK_JVMTI_ERROR(r, AddCapabilities);

    /* Set callbacks and enable event notifications */
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.Exception               = &cb_Exception;
    r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    CHECK_JVMTI_ERROR(r, SetEventCallbacks);

    /* Exception events */
    r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 
      JVMTI_EVENT_EXCEPTION, NULL);
    CHECK_JVMTI_ERROR(r, SetEventNotificationMode);

    return 0;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 2017-12-25
    • 2015-04-18
    • 2020-03-24
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多