有没有人知道如何以编程方式找出Java类加载器实际从哪里加载类?
我经常在大型项目上工作,其中类路径变得非常长,手动搜索不是一个真正的选项。 最近,我遇到了一个问题,因为同一类在类路径中存在两个不同的位置,导致类加载器加载了一个错误版本的类。
那么,我怎样才能让类加载器告诉我实际的类文件在磁盘上来自哪里呢?
编辑:如果类加载器由于版本不匹配(或其他原因)而无法加载类,我们是否有办法在读取文件之前找出它尝试读取的文件名?
有没有人知道如何以编程方式找出Java类加载器实际从哪里加载类?
我经常在大型项目上工作,其中类路径变得非常长,手动搜索不是一个真正的选项。 最近,我遇到了一个问题,因为同一类在类路径中存在两个不同的位置,导致类加载器加载了一个错误版本的类。
那么,我怎样才能让类加载器告诉我实际的类文件在磁盘上来自哪里呢?
编辑:如果类加载器由于版本不匹配(或其他原因)而无法加载类,我们是否有办法在读取文件之前找出它尝试读取的文件名?
这里是一个例子:
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
另一种无需操作源代码即可找出类加载位置的方法是使用以下选项启动Java虚拟机:-verbose:class
getClass().getProtectionDomain().getCodeSource().getLocation();
这是我们使用的内容:
public static String getClassResource(Class<?> klass) {
return klass.getClassLoader().getResource(
klass.getName().replace('.', '/') + ".class").toString();
}
这将取决于ClassLoader实现的情况:
getClass().getProtectionDomain().getCodeSource().getLocation()
当对象的 ClassLoader
注册为 null
时,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";
}
仅编辑第一行: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/
String className = MyClass.class.getName().replace(".", "/")+".class";
URL classUrl = MyClass.class.getClassLoader().getResource(className);
String fullPath = classUrl==null ? null : classUrl.getPath();
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
或者
字符串 obj = "simple test"; 系统。打印ln(obj.getClass().getResource(obj.getClass().getSimpleName()+".class"));
输出示例:
jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class
如果您需要定位名为Coverage.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,则位置将在target文件夹内。
如果您将应用程序构建为jar文件,则类的位置将是jar文件位置加上clazz包。
如果您将应用程序构建为war文件,则类的位置将是war文件位置(Tomcat中的/bin文件夹)加上clazz包。
Test.class.getResource("Test.class")
,它不会重复包名。 - meritonTest.class.getResource(Test.class.getSimpleName() + ".class")
- leonbloyBouncyCastleProvider
,需要完整的包名。 - Pavel VlasovgetClassLoader()
方法可能会返回null
。请参考这里中对该方法进行扩展来处理这种情况。 - OldCurmudgeon/target/test-classes
而不是/target/classes
加载资源。问题通过使用new MyClass().getClass().getResource("...")
- 或者在实例化上下文中只需使用this.getClass ...
来解决。 - Tomáš Zato