有人知道如何通过编程找到java类加载器实际从哪里加载类吗?

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

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

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


当前回答

当对象的ClassLoader被注册为空时,Jon的版本就失败了,这似乎意味着它是由Boot 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";
}

其他回答

当对象的ClassLoader被注册为空时,Jon的版本就失败了,这似乎意味着它是由Boot 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";
}

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

MyClass.class.getClassLoader();

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

对于单元测试

如果你需要一个叫做cover .java的类的位置,它位于包com.acme.api中

Class clazz = Coverage.class;
ClassLoader loader = clazz.getClassLoader();    
String coverageClazzCompiledAbsoluteTargetLocation = loader
.getResource(clazz.getCanonicalName().replace(".", File.separator) + ".class")
.getFile().replace(File.separatorChar + "$", "");
System.out.println(coverageClazzCompiledAbsoluteTargetLocation);

结果

/home/Joe/repositories/acme-api/target/test-classes/com/acme/api/Coverage.class

注意:由于java需要编译,您无法获得.java位置(src/main/java/…)您需要将.java编译为.class,因此如果您使用maven,位置将在目标文件夹内

运行时:JAR

如果你以jar的形式构建应用程序,类的位置将是jar的位置加上clazz包

运行时:WAR

如果你将应用构建为war,类的位置将是war位置(tomcat中的/bin文件夹)加上clazz包

只编辑第一行: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/

也许是糟糕的风格,但很好!

这种方法对文件和jar都有效:

Class clazz = Class.forName(nameOfClassYouWant);

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