[英]AbstractMethodError with dynamic java compilation and class loading with reflection
我正在尝试以下wrt Refleciton,让我知道是否可能
public class A {
void start(){
execute();
}
}
public class B extends A {
void execute()
{
doWork();
}
public abstract void doWork();
}
我将上述类打包在一个jar中,并使其在JVM上运行。 现在,我试图在运行时创建另一个类,在运行时对其进行编译,并尝试使用反射来调用B类的execute function()。
public class C extends B {
@Override
public void doWork()
{
//Implementation
}
}
反射代码:
Classloader =应用程序jar的URL和C.class的URL,在运行时进行编译。 父加载器-Thread.currentThread()。getContextClassLoader()我还将当前线程的上下文类加载器设置为上面创建的类加载器。
Class<? extends B> cls = (Class<? extends B>) Class.forName("className", true, clsLoader);
Object obj = cls.newInstance();
Method method = obj.getClass().getSuperclass().getMethod("start", new Class[0]);
method.invoke(obj, new Object[0]);
我能够获取该方法,并且调用也会被调用。 但是,当调用类B的execute时,它试图调用doWork(),然后遇到AbstractMethodError。 在查找异常后,我发现异常发生在错误的类加载器/ jar中。 但是我不确定如何解决这个问题。
有人可以协助吗?
首先,让我们澄清一个关于Thread
的上下文类加载器的神话:除非代码通过Thread.getContextClassLoader()
明确地要求该加载器(并使用它),否则它根本与类加载无关。
每个类都有一个初始类加载器,该加载器定义了该类,并且始终通过该初始类加载器解析对该类中其他类的引用。 这甚至适用于通过Class.forName(String)
反射性加载(没有显式的ClassLoader
); 它将使用调用者的初始化类加载器。
如果要通过需要访问B
的自定义类加载器加载C
,因为C
它的子类,那么确定父加载器以创建新加载器的最佳方法是B.class.getClassLoader()
。 但是,在您的简单设置中,它是与ClassLoader.getSystemClassLoader()
返回的ClassLoader.getSystemClassLoader()
相同,这也是Thread.getContextClassLoader()
返回的默认类加载器,这就是它起作用的原因。
您知道它起作用是因为您可以成功加载C
虽然其他引用可能会延迟解析,但直接超类必须立即解析,因此B
属于C
的加载器范围。
假设没有声明的execute()
在A
或abstract
的修饰class B
只是疏忽张贴的问题做出的,为什么调用方法不起作用的原因就简单多了:该方法abstract void doWork();
不是public
。
由于B
和C
由不同的类加载器加载,因此无论它们的限定名称如何,它们都被视为驻留在不同的程序包中。 因此,该方法doWork()
中C
不会覆盖该方法doWork()
在B
。 在编译时尚未检测到此情况,因为在编译时没有类加载器层次结构。 因此,编译器根据B
和C
的限定名称(或显式包声明)将其驻留在同一包中。 因此,编译器假定C.doWork()
实现B.doWork()
并且C
可以声明为非abstract
。
如果在B
中将方法doWork()
声明为public
,则它应该可以工作。 它应该更简单地工作:
try(URLClassLoader l = new URLClassLoader(new URL[]{/* url pointing to C */})) {
l.loadClass("C").asSubclass(B.class).newInstance().execute();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.