简体   繁体   English

动态Java编译和反射加载类的AbstractMethodError

[英]AbstractMethodError with dynamic java compilation and class loading with reflection

I am trying the following wrt Refleciton, let me know if this is possible 我正在尝试以下wrt Refleciton,让我知道是否可能

public class A {
    void start(){
       execute();
     }

}

public class B extends A {
    void execute()
    {
       doWork();
    }
    public abstract void doWork();
}

I have the above classes packaged in a jar and have it running on a JVM. 我将上述类打包在一个jar中,并使其在JVM上运行。 Now I am trying to create another class at run time, compile it at run time and trying to use reflection to invoke Class B's execute function(). 现在,我试图在运行时创建另一个类,在运行时对其进行编译,并尝试使用反射来调用B类的execute function()。

public class C extends B {
     @Override
     public void doWork()
     {
       //Implementation
     }
}

Reflection code: 反射代码:

Classloader = urls of application jars and url of C.class, compiled at run time. Classloader =应用程序jar的URL和C.class的URL,在运行时进行编译。 Parent loader - Thread.currentThread().getContextClassLoader() I am also setting the current thread's context class loader to the classloader created above. 父加载器-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]);   

I am able to get the method and the invoke also gets called. 我能够获取该方法,并且调用也会被调用。 However when class B's execute is called, it is trying to call the doWork() and then I run into an AbstractMethodError. 但是,当调用类B的execute时,它试图调用doWork(),然后遇到AbstractMethodError。 Upon looking up on the exception, I see that the exception happens with incorrect classloaders/jars. 在查找异常后,我发现异常发生在错误的类加载器/ jar中。 But I am not sure how do I go about fixing it in my case. 但是我不确定如何解决这个问题。

Can anyone assist? 有人可以协助吗?

First of all, let's clarify the myths about the context class loader of a Thread : unless code asks explicitly for that loader via Thread.getContextClassLoader() (and uses it), it has no relevance for class loading at all. 首先,让我们澄清一个关于Thread的上下文类加载器的神话:除非代码通过Thread.getContextClassLoader()明确地要求该加载器(并使用它),否则它根本与类加载无关。

Each class has an initiating class loader which defined the class and references to other classes within that class are always resolved via that initiating class loader. 每个类都有一个初始类加载器,该加载器定义了该类,并且始终通过该初始类加载器解析对该类中其他类的引用。 This even applies to reflective loading via Class.forName(String) (without an explicit ClassLoader ); 这甚至适用于通过Class.forName(String)反射性加载(没有显式的ClassLoader ); it will use the initiating class loader of the caller. 它将使用调用者的初始化类加载器。

If you want to load C via a custom class loader which needs to have access to B , because C subclasses it, the best way to determine the parent loader for the creation of your new loader is B.class.getClassLoader() . 如果要通过需要访问B的自定义类加载器加载C ,因为C它的子类,那么确定父加载器以创建新加载器的最佳方法是B.class.getClassLoader() However, in your simple setup, it's the same loader as returned by ClassLoader.getSystemClassLoader() which is also the default class loader as returned by Thread.getContextClassLoader() , that's why it worked. 但是,在您的简单设置中,它是与ClassLoader.getSystemClassLoader()返回的ClassLoader.getSystemClassLoader()相同,这也是Thread.getContextClassLoader()返回的默认类加载器,这就是它起作用的原因。

You know that it worked because you could load C successfully. 您知道它起作用是因为您可以成功加载C While other references might be resolved lazily, the direct superclass must be resolved immediately, so B is in scope of C 's loader. 虽然其他引用可能会延迟解析,但直接超类必须立即解析,因此B属于C的加载器范围。

Assuming that the absence of a declaration of execute() in A or an abstract modifier on class B are just oversights made by posting the question, the reason why invoking the method doesn't work is much simpler: the method abstract void doWork(); 假设没有声明的execute()Aabstract的修饰class B只是疏忽张贴的问题做出的,为什么调用方法不起作用的原因就简单多了:该方法abstract void doWork(); is not public . 不是public

Since B and C are loaded by different class loaders, they are considered to reside in different packages, regardless of their qualified name. 由于BC由不同的类加载器加载,因此无论它们的限定名称如何,它们都被视为驻留在不同的程序包中。 Therefore, the method doWork() in C does not override the method doWork() in B . 因此,该方法doWork()C不会覆盖该方法doWork()B This hasn't been detected at compile-time, as, at compile-time, there is no class loader hierarchy. 在编译时尚未检测到此情况,因为在编译时没有类加载器层次结构。 So the compiler considers B and C to reside in the same package, based on their qualified name (or explicit package declaration). 因此,编译器根据BC的限定名称(或显式包声明)将其驻留在同一包中。 Therefore the compiler assumes that C.doWork() implements B.doWork() and C can be declared non- abstract . 因此,编译器假定C.doWork()实现B.doWork()并且C可以声明为非abstract

If you declare the method doWork() in B as public , it should work. 如果在B中将方法doWork()声明为public ,则它应该可以工作。 And it should work much simpler: 它应该更简单地工作:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM