关于classpath:查找从哪里加载java类

Find where java class is loaded from

有人知道如何编程地找出Java类加载器从何处加载该类吗?

我经常在大型项目中工作,在这些项目中,类路径非常长,手动搜索实际上不是一个选项。我最近遇到了一个问题,类加载器加载了一个不正确的类版本,因为它位于类路径的两个不同位置。

那么,如何让类加载器告诉我实际类文件在磁盘上的来源呢?

编辑:如果由于版本不匹配(或其他原因),类加载器实际上无法加载类,那么我们是否可以在读取之前找到它尝试读取的文件?


下面是一个例子:

1
2
3
4
5
6
7
8
9
10
package foo;

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

打印出来:

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


另一种方法是从(不操纵源)加载一个类的方法是用选项EDCOX1(2)启动Java VM。


1
getClass().getProtectionDomain().getCodeSource().getLocation();


这就是我们使用的:

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

这将取决于类加载器的实现:getClass().getProtectionDomain().getCodeSource().getLocation()


当对象的ClassLoader注册为null时,jon的版本失败,这似乎意味着它是由启动ClassLoader加载的。

此方法处理该问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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";
}

只编辑第一行:Main类。

1
2
3
4
Class<?> c = Main.class;
String path = c.getResource(c.getSimpleName() +".class").getPath().replace(c.getSimpleName() +".class","");

System.out.println(path);

输出:

1
/C:/Users/Test/bin/

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


通常,我们不知道使用什么硬编码。我们可以先获取类名,然后使用ClassLoader获取类URL。

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

看看这个类似的问题。发现同一类的工具..

我认为最相关的障碍是如果您有一个定制的类加载器(从DB或LDAP加载)


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

1
2
3
4
Class clazz = Class.forName(nameOfClassYouWant);

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

假设您使用的是一个名为MyClass的类,那么下面的内容应该有效:

1
MyClass.class.getClassLoader();

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