【问题标题】:Find where java class is loaded from查找从哪里加载 java 类
【发布时间】:2010-09-18 15:43:06
【问题描述】:

有谁知道如何以编程方式找出 java 类加载器实际从哪里加载类?

我经常从事大型项目,其中类路径变得很长,手动搜索并不是一个真正的选择。我最近有一个problem,其中类加载器加载了一个不正确的类版本,因为它位于两个不同位置的类路径中。

那么如何让类加载器告诉我实际的类文件来自磁盘的哪个位置?

编辑: 如果类加载器实际上由于版本不匹配(或其他原因)而无法加载类怎么办,无论如何我们可以找出什么文件它在阅读之前尝试阅读?

【问题讨论】:

    标签: java classpath classloader


    【解决方案1】:

    假设您正在使用一个名为 MyClass 的类,以下应该可以工作:

    MyClass.class.getClassLoader();
    

    能否获得 .class 文件的磁盘位置取决于类加载器本身。例如,如果您使用的是 BCEL 之类的东西,那么某个类甚至可能没有磁盘表示。

    【讨论】:

    • 这会返回用于加载类的ClassLoader,不是吗?它没有找到 .class 文件在哪里?
    • 不,它没有。类加载器实际上可以引用完全不同的类路径——这意味着它将完全无法获取实际的类位置。
    【解决方案2】:

    看看这个类似的问题。 Tool to discover same class..

    我认为最相关的障碍是您是否有自定义类加载器(从 db 或 ldap 加载)

    【讨论】:

      【解决方案3】:

      这是一个例子:

      package foo;
      
      public class Test
      {
          public static void main(String[] args)
          {
              ClassLoader loader = Test.class.getClassLoader();
              System.out.println(loader.getResource("foo/Test.class"));
          }
      }
      

      打印出来的:

      file:/C:/Users/Jon/Test/foo/Test.class
      

      【讨论】:

      • 为了减少多余的输入,也可以使用较短的版本:Test.class.getResource("Test.class"),它不会重复包名。
      • 如果类被编译了怎么办,例如来自 .groovy 文件?
      • @meriton:或者,为了在重构中存活:Test.class.getResource(Test.class.getSimpleName() + ".class")
      • 对于BouncyCastleProvider,需要完整的包名。
      • getClassLoader() 可以返回null。请参阅here 了解对此方法的扩展以处理该问题。
      【解决方案4】:
      getClass().getProtectionDomain().getCodeSource().getLocation();
      

      【讨论】:

      • 是的,尽管它不适用于已安装的安全管理器且没有所需的权限。
      • 仅供参考,NPE = 空指针异常。 HTH!
      • 只要你有一个实例的引用,这个方法是首选的,因为你可以从两个不同的位置加载同一个类。
      • 从 Java 9+ 模块调用时也不起作用(当然你在 2008 年不可能知道)。
      【解决方案5】:

      这是我们使用的:

      public static String getClassResource(Class<?> klass) {
        return klass.getClassLoader().getResource(
           klass.getName().replace('.', '/') + ".class").toString();
      }
      

      这将取决于 ClassLoader 实现: getClass().getProtectionDomain().getCodeSource().getLocation()

      【讨论】:

        【解决方案6】:

        另一种找出类从何处加载(无需操作源)的方法是使用以下选项启动 Java VM:-verbose:class

        【讨论】:

        • 这个效果很好,并且不存在处理带有 null ClassLoader 的类的问题
        • @ries 如果不需要以编程方式执行此操作,这绝对是要走的路,它确实解决了我的问题。但是,OP 专门询问了如何以编程方式执行此操作。
        【解决方案7】:

        当对象的ClassLoader 注册为null 时,Jon 的版本失败,这似乎暗示它是由引导ClassLoader 加载的。

        这个方法可以解决这个问题:

        public static String whereFrom(Object o) {
          if ( o == null ) {
            return null;
          }
          Class<?> c = o.getClass();
          ClassLoader loader = c.getClassLoader();
          if ( loader == null ) {
            // Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
            loader = ClassLoader.getSystemClassLoader();
            while ( loader != null && loader.getParent() != null ) {
              loader = loader.getParent();
            }
          }
          if (loader != null) {
            String name = c.getCanonicalName();
            URL resource = loader.getResource(name.replace(".", "/") + ".class");
            if ( resource != null ) {
              return resource.toString();
            }
          }
          return "Unknown";
        }
        

        【讨论】:

          【解决方案8】:

          仅编辑第一行:Main.class

          Class<?> c = Main.class;
          String path = c.getResource(c.getSimpleName() + ".class").getPath().replace(c.getSimpleName() + ".class", "");
          
          System.out.println(path);
          

          输出:

          /C:/Users/Test/bin/
          

          也许风格不好,但效果很好!

          【讨论】:

            【解决方案9】:

            这种方法适用于文件和 jar:

            Class clazz = Class.forName(nameOfClassYouWant);
            
            URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
            InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
            

            【讨论】:

              【解决方案10】:

              通常,我们不使用硬编码。我们可以先获取className,然后使用ClassLoader获取class URL。

                      String className = MyClass.class.getName().replace(".", "/")+".class";
                      URL classUrl  = MyClass.class.getClassLoader().getResource(className);
                      String fullPath = classUrl==null ? null : classUrl.getPath();
              

              【讨论】:

              • 需要为:URL classUrl = MyClass.class.getClassLoader().getResource("/" + className);
              • MyClass.class 是重要的部分 - getClass() 可以返回代理!然后你可以得到像 MyClass$$EnhancerBySpringCGLIB$$a98db882.class 这样的名字和空 URL。
              【解决方案11】:

              简单的方法:

              System.out.println(java.lang.String.class.getResource(String.class.getSimpleName()+".class"));

              输出示例:

              jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class

              或者

              String obj = "简单测试"; System.out.println(obj.getClass().getResource(obj.getClass().getSimpleName()+".class"));

              输出示例:

              jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class

              【讨论】:

                猜你喜欢
                • 2020-01-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-07-17
                • 1970-01-01
                • 2020-10-21
                相关资源
                最近更新 更多