我想在运行时为使用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
时,一切正常,但这并不能解决我的具体问题。)
答案 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());