如何使用 bytebuddy 在现有 class 中定义新方法并在同一 class 中的现有方法中添加对它的调用?
How to define a new method inside an existing class and add a call to it in an existing method inside the same class using bytebuddy?
我有一个 class 如下所示
public class HelloWorld{
public void sayHelloWorld(){
System.out.println("Hello World");
}
}
现在我想使用 bytebuddy 添加另一个方法到 HelloWorld class 并在 sayHelloWorld[= 中添加对新方法的调用25=]。所以假设 class 在 bytebuddy 施展魔法之后看起来像这样。 (我知道 bytebuddy 使用字节码而不是 java 源文件。下面的代码仅用于说明目的。)
public class HelloWorld{
public void sayHelloWorld(){
System.out.println("Hello World");
sayHelloAgain()
}
public void sayHelloAgain(){
System.out.println("Hello Again")
}
}
- 首先,bytebuddy 可以吗?
- 其次,如果可以,我该怎么做?我了解到bytebuddy可以用来重新定义方法,但不能修改方法体。这是真的吗?
如果有人能阐明这一点,那就太好了。 TIA!
Byte Buddy 允许您以多种方式执行此操作。最直接的方法是定义一个实现 sayHelloAgain
的拦截器,Byte Buddy 向其创建委托:
public class HelloAgainDelegate {
public static void sayHelloAgain() {
System.out.println("Hello again");
}
}
然后您可以在重新定义的 class 上定义方法并重新设置 sayHelloWorld
方法的基线以首先调用原始方法,然后调用另一个方法:
Class<?> type = new ByteBuddy()
.rebase(HelloWorld.class)
.defineMethod("sayHelloAgain", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(HelloAgainDelegate.class))
.method(named("sayHelloWorld"))
.intercept(SuperMethodCall.INSTANCE
.andThen(MethodCall.invoke(named("sayHelloAgain"))))
.make()
.load(HelloWorld.class.getClassLoader(),
ClassLoadingStrategy.Default.CHILD_FIRST)
.getLoaded();
Object instance = type.newInstance();
type.getMethod("sayHelloWorld").invoke(instance);
type.getMethod("sayHelloAgain").invoke(instance);
在 Java 代码中,变基后的 class 看起来像这样:
public class HelloWorld {
synthetic void sayHelloWorld$origin() {
System.out.println("Hello World");
}
public void sayHelloWorld() {
sayHelloWorld$origin();
sayHelloAgain();
}
public void sayHelloAgain() {
HelloAgainInterceptor.sayHelloAgain();
}
}
如果此委托不适合您,您还可以使用 Advice
内联模板中的代码 class:
class HelloAgainAdvice {
@Advice.OnMethodExit
static void sayHelloAgain() {
System.out.println("Hello again");
}
}
而不是 MethodDelegation
,您可以通过 Advice.to(HelloAgainAdvice.class)
使用此 class。由于代码被复制,您将无法设置断点,但您重新定义的 class 将是独立的。
我有一个 class 如下所示
public class HelloWorld{
public void sayHelloWorld(){
System.out.println("Hello World");
}
}
现在我想使用 bytebuddy 添加另一个方法到 HelloWorld class 并在 sayHelloWorld[= 中添加对新方法的调用25=]。所以假设 class 在 bytebuddy 施展魔法之后看起来像这样。 (我知道 bytebuddy 使用字节码而不是 java 源文件。下面的代码仅用于说明目的。)
public class HelloWorld{
public void sayHelloWorld(){
System.out.println("Hello World");
sayHelloAgain()
}
public void sayHelloAgain(){
System.out.println("Hello Again")
}
}
- 首先,bytebuddy 可以吗?
- 其次,如果可以,我该怎么做?我了解到bytebuddy可以用来重新定义方法,但不能修改方法体。这是真的吗?
如果有人能阐明这一点,那就太好了。 TIA!
Byte Buddy 允许您以多种方式执行此操作。最直接的方法是定义一个实现 sayHelloAgain
的拦截器,Byte Buddy 向其创建委托:
public class HelloAgainDelegate {
public static void sayHelloAgain() {
System.out.println("Hello again");
}
}
然后您可以在重新定义的 class 上定义方法并重新设置 sayHelloWorld
方法的基线以首先调用原始方法,然后调用另一个方法:
Class<?> type = new ByteBuddy()
.rebase(HelloWorld.class)
.defineMethod("sayHelloAgain", void.class, Visibility.PUBLIC)
.intercept(MethodDelegation.to(HelloAgainDelegate.class))
.method(named("sayHelloWorld"))
.intercept(SuperMethodCall.INSTANCE
.andThen(MethodCall.invoke(named("sayHelloAgain"))))
.make()
.load(HelloWorld.class.getClassLoader(),
ClassLoadingStrategy.Default.CHILD_FIRST)
.getLoaded();
Object instance = type.newInstance();
type.getMethod("sayHelloWorld").invoke(instance);
type.getMethod("sayHelloAgain").invoke(instance);
在 Java 代码中,变基后的 class 看起来像这样:
public class HelloWorld {
synthetic void sayHelloWorld$origin() {
System.out.println("Hello World");
}
public void sayHelloWorld() {
sayHelloWorld$origin();
sayHelloAgain();
}
public void sayHelloAgain() {
HelloAgainInterceptor.sayHelloAgain();
}
}
如果此委托不适合您,您还可以使用 Advice
内联模板中的代码 class:
class HelloAgainAdvice {
@Advice.OnMethodExit
static void sayHelloAgain() {
System.out.println("Hello again");
}
}
而不是 MethodDelegation
,您可以通过 Advice.to(HelloAgainAdvice.class)
使用此 class。由于代码被复制,您将无法设置断点,但您重新定义的 class 将是独立的。