Byte Buddy:为抽象类创建实现

时间:2016-01-10 18:38:18

标签: java class interface bytecode byte-buddy

我想在运行时为使用Byte Buddy的抽象类创建一个实现,我面临的问题是,从创建的实例调用方法时会抛出def remind_user_to_do_something users = User.get_users_based_on_condition // The above could be a scope defined on your User class. // Basically that scope would check 2 things // 1. Do I meet the condition you're talking about above. // 2. Is it time to send an email to them based on their timezone. for each user in users MyActionMailerClass.send_email_for_user user end end 。我有一个像这样的java.lang.AbstractMethodError类(我实际上无法修改,实际上包含更多逻辑):

abstract

使用以下最小样本,我希望我的public abstract class Algorithm { abstract int execute(); } 实例返回一个常量值:

Algorithm

然而,这导致以下例外:

Class<?> type = new ByteBuddy()
                        .subclass(Algorithm.class)
                        .method(ElementMatchers.named("execute"))
                        .intercept(FixedValue.value(42))
                        .make()
                        .load(classLoader, ClassLoadingStrategy.Default.WRAPPER)
                        .getLoaded();
Algorithm instance = (Algorithm) type.newInstance();
System.out.println(myInstance.execute());

(当我通过实验将Exception in thread "main" java.lang.AbstractMethodError: package.Algorithm.execute()I 更改为Algorithm时,一切正常,但这并不能解决我的具体问题。)

1 个答案:

答案 0 :(得分:13)

如果您使用相同的类加载器设置使用 javac 生成相同的代码,那么您可能会感到惊讶。您在JLS中如何指定package-privacy暗示了您所观察到的内容。您的非界面

public abstract class Algorithm {
  abstract int execute(); 
}

定义了一个package-private方法。由于您没有为生成的类定义自定义名称,因此Byte Buddy会生成一个具有随机名称的子类,该子类位于同一个包中。 Byte Buddy进一步发现executable方法可以从生成的子类中重写,并完全按照您的预期实现它。

但是,您正在使用ClassLoadingStrategy.Default.WRAPPER策略来加载类,该类创建一个加载Algorithm的子类加载器。在Java中,在运行时,如果包的名称相同且两个包都由相同的ClassLoader 加载,则两个包仅相等。后一种情况不适用于您的情况,因此JVM不再将多态性应用于execute类。致电

((Algorithm) type.newInstance()).execute();

因此,您不是在调用生成的方法,而是调用原始的抽象方法。因此 - 根据JLS - 抛出AbstractMethodError

要解决此问题,您需要使用默认的INJECTION策略在同一个包中加载生成的类,或者您必须将execute定义为public(这是在定义接口时隐式)或protected方法,以便您期望的多态性规则适用。作为第三个选项,您可以通过

调用正确的运行时方法
type.getDeclaredMethod("execute").invoke(type.newInstance());