查找从哪里加载 java 类
Posted
技术标签:
【中文标题】查找从哪里加载 java 类【英文标题】:Find where java class is loaded from 【发布时间】:2010-09-18 15:43:06 【问题描述】:有谁知道如何以编程方式找出 java 类加载器实际从哪里加载类?
我经常从事大型项目,其中类路径变得很长,手动搜索并不是一个真正的选择。我最近有一个problem,其中类加载器加载了一个不正确的类版本,因为它位于两个不同位置的类路径中。
那么如何让类加载器告诉我实际的类文件来自磁盘的哪个位置?
编辑: 如果类加载器实际上由于版本不匹配(或其他原因)而无法加载类怎么办,无论如何我们可以找出什么文件它在阅读之前尝试阅读?
【问题讨论】:
【参考方案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
【讨论】:
以上是关于查找从哪里加载 java 类的主要内容,如果未能解决你的问题,请参考以下文章
28 Java类的加载机制什么是类的加载类的生命周期加载:查找并加载类的二进制数据连接初始化类加载器双亲委派模型自定义类加载器