Java 如何探索从哪些JAR加载哪些类?

Java 如何探索从哪些JAR加载哪些类?,java,profiling,classloader,jar,Java,Profiling,Classloader,Jar,有没有办法确定在运行时从哪个JAR加载哪些类 我敢肯定我们以前都进过地狱。我在项目中多次遇到过此问题的疑难解答ClassNotFoundExceptions和noclassdefounderrors。我希望避免在JAR中找到类的所有实例,并对代码使用消除过程,从而导致CNFE找到罪魁祸首 任何分析或管理工具都会提供此类信息吗 这个问题非常烦人,因为我们应该在类被加载时获得这些信息。必须有办法找到它,或者记录下来并找到它,但我知道没有什么能做到这一点,你说呢 我知道OSGi和版本化的bundle/

有没有办法确定在运行时从哪个JAR加载哪些类

我敢肯定我们以前都进过地狱。我在项目中多次遇到过此问题的疑难解答
ClassNotFoundException
s和
noclassdefounderror
s。我希望避免在JAR中找到类的所有实例,并对代码使用消除过程,从而导致CNFE找到罪魁祸首

任何分析或管理工具都会提供此类信息吗

这个问题非常烦人,因为我们应该在类被加载时获得这些信息。必须有办法找到它,或者记录下来并找到它,但我知道没有什么能做到这一点,你说呢

我知道OSGi和版本化的bundle/modules旨在使这成为一个非问题。。。但它似乎不会很快消失

注意:我发现这是我问题的一个子集,与从版本化JAR加载的类有关

这篇文章有点相关,它解释了一种在JAR(当前目录下)或M2_REPO中搜索类的策略:


也有点相关,

-verbose:class
开关传递到
java
命令将打印加载的每个类及其加载位置


也是提前查找缺失类的好工具。

从代码中可以调用:

myObject.getClass().getProtectionDomain().getCodeSource()

(注意,
getProtectionDomain
可能不幸地返回
null
(设计不好),因此“正确的代码”会检查该值。)

上面Jason Day提到的JVM标志有一个MBean

如果您使用的是JBoss,那么如果您将本机JMX MBean服务器添加到配置中,就可以使用JMX按需处理这个问题。添加以下-D:

-Dcom.sun.management.jmxremote.port=3333
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djboss.platform.mbeanserver 
-Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl
-DJBOSS_CLASSPATH="../lib/jboss-system-jmx.jar"
然后您可以在java.lang:Classloading MBean下看到这个设置,并可以动态地打开/关闭它。如果您只想在执行某段代码时打开它,这将非常有用

还有一个MBean,它允许您输入一个完全限定的类名,并查看它在类层次结构中的加载位置。MBean称为LoaderRepository,您需要调用displayClassInfo()操作,传入FQCN。

在WebSphere(WAS)中,您可以使用名为“类装入器查看器”的功能

通过单击服务器>服务器类型>WebSphere应用程序服务器>服务器名称>类加载器查看器服务,首先启用类加载器查看器,启用服务并重新启动服务器

然后,您可以转到疑难解答>类加载器查看器并搜索您的类或包名称


您可以轻松导出JMX操作,以访问进程中任何已加载类的包信息,如:

  public static final class Jmx {

    @JmxExport
    public static Reflections.PackageInfo getPackageInfo(@JmxExport("className") final String className) {
      return Reflections.getPackageInfo(className);
    }
  }
下面是一个导出和调用它的简单单元测试:

  @Test
  public void testClassLocator() throws IOException, InstanceNotFoundException, MBeanException, ReflectionException {
    Registry.export(Jmx.class);
    Reflections.PackageInfo info = (Reflections.PackageInfo) Client.callOperation(
            "service:jmx:rmi:///jndi/rmi://:9999/jmxrmi",
            Jmx.class.getPackage().getName(),
            Jmx.class.getSimpleName(), "getPackageInfo", Registry.class.getName());
    System.out.println(info);
    Assert.assertNotNull(info);
  }
这一切都基于使用spf4j()中的一些小实用程序库


您可以看到此代码和测试

这对CNFE或NCDFE没有帮助;你什么时候做的?如果你得到一个ClassNotFoundException,那么你可能已经知道这个类不在任何JAR中了。(假设您没有使用类加载器。)如果在类加载过程中出现其他错误,例如缺少超类,则可能会出现问题。@TomHawtin tackline如果所述类的静态初始值设定项引发异常,您可以获得
ClassNotFoundException
。然后您在jar文件中有了类,但类加载器无法加载它。如果您不想(或不能)在Eclipse调试表达式中使用它修改代码。很好-Joops看起来很酷-你知道它是否足够聪明,可以跟随通过class.forName引用的类吗?如果它可以跟随class.forName引用,我会感到惊讶,但我不确定。Joops还有一个
命令,它
命令,因此您可以使用它来手动按名称检查类。+1非常感谢!帮助我解决了类加载冲突!如果已经在运行我无法控制的JVM呢?i、 我不能用-verbose:class选项重新启动它。这可能是不可能的,因为jvm从来不会在没有-verbose:class选项的情况下保存日志,但只是想知道。Jason Day说得对,这基本上是我不久前问的一个问题的重复。