Chain/transform 使用 ByteBuddy 调用方法
Chain/transform method calls with ByteBuddy
使用 ByteBuddy,我可以通过调用另一个实例方法并运行形成结果来实现一个实例方法吗?
例如(玩具示例):
public abstract class Foo {
public String bar() {
return "bar";
}
public abstract int baz();
}
鉴于上述情况,我能否实现 baz
,使其调用 bar()
和 returns returned 字符串的长度?即,就好像它是:
public int baz() {
return bar().length();
}
我天真地尝试了以下方法:
Method bar = Foo.class.getDeclaredMethod("bar");
Method baz = Foo.class.getDeclaredMethod("baz");
Method length = String.class.getDeclaredMethod("length");
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(
MethodCall.invoke(bar) // call bar()...
.andThen(MethodCall.invoke(length)) // ... .length()?
).make()
.load(Foo.class.getClassLoader())
.getLoaded()
.newInstance();
System.out.println(foo.baz());
但是,我认为 andThen()
是在第一次调用的 return 值上调用的,这似乎是错误的;它看起来像是在生成的实例上调用的。
Exception in thread "main" java.lang.IllegalStateException:
Cannot invoke public int java.lang.String.length() on class Foo$ByteBuddy$sVgjXXp9
at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation
.invoke(MethodCall.java:1667)
我也试过一个拦截器:
class BazInterceptor {
public static int barLength(@This Foo foo) {
String bar = foo.bar();
return bar.length();
}
}
与:
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(MethodDelegation.to(new BazInterceptor()))
// ...etc.
这个 运行,但产生了无意义的结果 870698190
,并且设置断点 and/or 在 barLength()
中添加打印语句表明它永远不会被调用;很明显,我也没有正确理解拦截器或 @This
。
如何让 ByteBuddy 调用一种方法,然后根据其 return 值调用另一种方法?
Per : BazInterceptor
工作如果 :
- 我们像上面一样委托给
new BazInterceptor()
,但是使 barLength()
成为一个实例方法,或者:
- 我们留下
barLength()
一个 class 方法,但委托给 BazInterceptor.class
而不是实例。
我怀疑 870698190
委托给了 BazInterceptor
实例的 hashCode()
,虽然我没有实际检查过。
您使用实例作为拦截器,这意味着实例方法是首选(可能根本不接受静态方法)。有一个实例方法与您的 int baz()
方法的签名匹配,它是 int hashCode()
。您得到的数字是 new BazInterceptor()
实例的哈希码。
我知道的选项:
- 从
barLength
中删除 static
这样它实际上将用于拦截。
- 添加 class 作为拦截器
.intercept(MethodDelegation.to(BazInterceptor.class))
我更喜欢第二个选项,因为您没有使用 BazInterceptor
实例的任何 fields/state。
目前在 Byte Buddy 中没有很好的方法,但这将是一个很容易添加的功能。你可以track the progress on GitHub。等我有时间再补充。
如果您现在想实现这样的链式调用,您可以在 Java 代码中实现它们并使用 Advice
组件内联此代码。或者,您可以通过基于 MethodInvocation
实例创建自己的 ByteCodeAppender
来更明确地编写字节代码,但是您必须手动加载参数。
使用 ByteBuddy,我可以通过调用另一个实例方法并运行形成结果来实现一个实例方法吗?
例如(玩具示例):
public abstract class Foo {
public String bar() {
return "bar";
}
public abstract int baz();
}
鉴于上述情况,我能否实现 baz
,使其调用 bar()
和 returns returned 字符串的长度?即,就好像它是:
public int baz() {
return bar().length();
}
我天真地尝试了以下方法:
Method bar = Foo.class.getDeclaredMethod("bar");
Method baz = Foo.class.getDeclaredMethod("baz");
Method length = String.class.getDeclaredMethod("length");
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(
MethodCall.invoke(bar) // call bar()...
.andThen(MethodCall.invoke(length)) // ... .length()?
).make()
.load(Foo.class.getClassLoader())
.getLoaded()
.newInstance();
System.out.println(foo.baz());
但是,我认为 andThen()
是在第一次调用的 return 值上调用的,这似乎是错误的;它看起来像是在生成的实例上调用的。
Exception in thread "main" java.lang.IllegalStateException:
Cannot invoke public int java.lang.String.length() on class Foo$ByteBuddy$sVgjXXp9
at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation
.invoke(MethodCall.java:1667)
我也试过一个拦截器:
class BazInterceptor {
public static int barLength(@This Foo foo) {
String bar = foo.bar();
return bar.length();
}
}
与:
Foo foo = new ByteBuddy()
.subclass(Foo.class)
.method(ElementMatchers.is(baz))
.intercept(MethodDelegation.to(new BazInterceptor()))
// ...etc.
这个 运行,但产生了无意义的结果 870698190
,并且设置断点 and/or 在 barLength()
中添加打印语句表明它永远不会被调用;很明显,我也没有正确理解拦截器或 @This
。
如何让 ByteBuddy 调用一种方法,然后根据其 return 值调用另一种方法?
Per BazInterceptor
工作如果 :
- 我们像上面一样委托给
new BazInterceptor()
,但是使barLength()
成为一个实例方法,或者: - 我们留下
barLength()
一个 class 方法,但委托给BazInterceptor.class
而不是实例。
我怀疑 870698190
委托给了 BazInterceptor
实例的 hashCode()
,虽然我没有实际检查过。
您使用实例作为拦截器,这意味着实例方法是首选(可能根本不接受静态方法)。有一个实例方法与您的 int baz()
方法的签名匹配,它是 int hashCode()
。您得到的数字是 new BazInterceptor()
实例的哈希码。
我知道的选项:
- 从
barLength
中删除static
这样它实际上将用于拦截。 - 添加 class 作为拦截器
.intercept(MethodDelegation.to(BazInterceptor.class))
我更喜欢第二个选项,因为您没有使用 BazInterceptor
实例的任何 fields/state。
目前在 Byte Buddy 中没有很好的方法,但这将是一个很容易添加的功能。你可以track the progress on GitHub。等我有时间再补充。
如果您现在想实现这样的链式调用,您可以在 Java 代码中实现它们并使用 Advice
组件内联此代码。或者,您可以通过基于 MethodInvocation
实例创建自己的 ByteCodeAppender
来更明确地编写字节代码,但是您必须手动加载参数。