如何在现有类中定义一个新方法,并使用bytebuddy在同一个类内的现有方法中添加对它的调用?

时间:2017-09-23 22:42:42

标签: java byte-buddy

我有一个类似于下面的课程

public class HelloWorld{
  public void sayHelloWorld(){
    System.out.println("Hello World");
  }
}

现在我想使用bytebuddy向 HelloWorld 类添加另一种方法,并在 sayHelloWorld 中添加对新方法的调用。因此假设在bytebuddy做出它的魔力之后,这个类看起来就像这样。 (我知道bytebuddy适用于字节码而不是java源文件。下面的代码仅用于说明目的。)

public class HelloWorld{
  public void sayHelloWorld(){
    System.out.println("Hello World");
    sayHelloAgain()
  }
  public void sayHelloAgain(){
    System.out.println("Hello Again")
  }
}
  1. 首先,这可能是bytebuddy吗?
  2. 其次,如果有可能,我该怎么办?我已经理解了bytebuddy可以用来重新定义方法,但不能修改方法体。这是真的吗?
  3. 如果有人能够对此有所了解,那就太好了。 TIA!

1 个答案:

答案 0 :(得分:2)

Byte Buddy允许您以各种方式执行此操作。最直接的方法是定义一个实现sayHelloAgain的拦截器,Byte Buddy创建一个委托:

public class HelloAgainDelegate {
  public static void sayHelloAgain() {
    System.out.println("Hello again");
  }
}

然后,您可以在重新定义的类上定义方法并重新定义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代码中,重新定义的类看起来像这样:

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 HelloAgainAdvice {
  @Advice.OnMethodExit
  static void sayHelloAgain() {
    System.out.println("Hello again");
  }
}

您可以通过MethodDelegation使用此类,而不是Advice.to(HelloAgainAdvice.class)。在复制代码时,您将无法设置断点,但重新定义的类将是自包含的。