Java 如果缺少类依赖项,如何确定缺少哪个类?

Java 如果缺少类依赖项,如何确定缺少哪个类?,java,command-line-interface,classloader,Java,Command Line Interface,Classloader,我得到错误“无法找到或加载主类”,即使该类存在。从中,我了解到,由于缺少依赖项,很可能可以找到该类,但无法加载该类 除了手动反编译类,检查它的所有依赖项,以及这些依赖项是否在类路径上,等等,对于每个依赖类,是否有任何方法可以确定类路径中缺少哪个类,从而导致Java无法加载我的主类 另一个问题是缺少父/接口类的问题。但是我已经手动检查了所有的祖先类是否在指定的类路径上或者在JDK中,如下所示 $ cd ~/picketbox $ java -cp picketbox-4.1.1.Final-red

我得到错误“无法找到或加载主类”,即使该类存在。从中,我了解到,由于缺少依赖项,很可能可以找到该类,但无法加载该类

除了手动反编译类,检查它的所有依赖项,以及这些依赖项是否在类路径上,等等,对于每个依赖类,是否有任何方法可以确定类路径中缺少哪个类,从而导致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 {
$
创建帮助器类:

public class Helper {
    public static void main(String[] args) {
        YourActualMainClass.main(args);
    }
}
并尝试运行此帮助器类,而不是
YourActualMainClass

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

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



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

您可以尝试使用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)
{
}
#包括
#包括
#包括
#包括
#包括
#定义检查JVMTI错误(x,调用)\
{如果(x!=JVMTI_ERROR_NONE){fprintf(stderr,“在%s中的%s期间出错:%d\n”,#调用,_文件,_行_;}
/*全局静态数据*/
静态jvmtiEnv*jvmti;
静态无效JNICALL cb_异常(jvmtiEnv*jvmti_env、JNIEnv*jni_env、,
jthread线程,jmethodID方法,jlocation位置,jobject异常,
jmethodID catch_方法,jlocation catch_位置)
{
jclass exceptionClass=(*jni_env)->GetObjectClass(jni_env,exception);
jmethodID=(*jni_env)->GetMethodID(jni_env,exceptionClass,
“printStackTrace”,
“()V”);
(*jni_env)->CallVoidMethod(jni_env,exception,methodId);
}
JNIEXPORT jint JNICALL代理程序\u OnLoad(JavaVM*vm,char*options,void*reserved)
{
金特钢筋混凝土;
jvmtiError;
JVM能力;
jvmtiEventCallbacks回调;
/*获取JVMTI环境*/
rc=(*vm)->GetEnv(vm,(void**)和jvmti,jvmti_版本);
如果(rc!=JNI_OK)
{
fprintf(stderr,“Error!:无法创建jvmtiEnv,rc=%d\n”,rc);
返回-1;
}
/*获取/添加JVMTI功能*/
memset(&capabilities,0,sizeof(capabilities));
capabilities.can_生成异常事件=1;
r=(*jvmti)->添加能力(jvmti和能力);
检查JVMTI_错误(r,AddCapabilities);
/*设置回调并启用事件通知*/
memset(&callbacks,0,sizeof(callbacks));
callbacks.Exception=&cb_Exception;
r=(*jvmti)->SetEventCallbacks(jvmti,&callbacks,sizeof(callbacks));
检查JVMTI_错误(r,SetEventCallbacks);
/*异常事件*/
r=(*jvmti)->SetEventNotificationMode(jvmti,jvmti_ENABLE,
JVMTI_事件(异常,空);
检查JVMTI错误(r,SetEventNotificationMode);
返回0;
}
JNIEXPORT void JNICALL代理程序(JavaVM*vm)
{
}

您是否尝试使用-XX:+TraceClassLoading选项?@ClaudioCorsi我以前不知道该选项。我刚才试过,但不幸的是根本没用。它只是显示Java从
rt.jar
加载了很多JDK类,但仍然给出了相同的错误消息。我试图将输出作为编辑附加到我的问题,但不幸的是,它导致我的问题超出了字符限制。但是这里面没有什么有用的东西,只是一堆行的形式
[Loaded x.y.z.ClassName from/../rt.jar]
。另一个选项是,您可以实现一个jvmti模块并处理exceptionthrown事件。然后可以调用异常的printStackTrace方法。希望这能提供一条线索,说明缺少了哪个类。@ClaudioCorsi对不起,我不知道怎么做;我甚至从未听说过jvmti。你能把你的评论作为一个答案发表出来,并详细说明一下吗?不幸的是,这没有帮助(没有双关语的意思)。它只是说“无法找到或加载主类助手”。然而,我发现JAR本身可能以某种方式被损坏(不确定如何被提取,因为它仍然可以被提取)。如果我提取JAR,然后重新压缩它(即
jarxvf
jarcvf
),它就会工作。此外,我必须重新构建,以便能够编译
Helper.java
,否则
javac
会报告“打开zip文件时出错”