【问题标题】: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?如何在现有类中定义新方法并使用 bytebuddy 在同一类中的现有方法中添加对它的调用?
【发布时间】:2017-09-23 22:42:42
【问题描述】:

我有一个如下所示的课程

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 可以用来重新定义方法,但不能修改方法体。这是真的吗?

如果有人能对此有所了解,那就太好了。蒂亚!

【问题讨论】:

  • 1) 你需要他们所谓的变基 (bytebuddy.net/javadoc/1.7.5/net/bytebuddy/…) 2) 查看他们的教程 (bytebuddy.net/#/tutorial),“重新定义和变基现有类”部分说您使用与子类化相同的语法 (他们详细讨论),除了前面提到的 .rebase 方法的起点。但是,修改现有方法似乎是不可能的,我认为这只是修改现有类。但是可以替换,但是您必须重新构建完整的方法
  • 谢谢。但是替换整个方法体对我来说不是一个选择,因为我只能访问字节码并且操作字节码非常困难,我希望有一种更清洁、更简单的方法来修改方法。

标签: java byte-buddy


【解决方案1】:

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");
  }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-14
    • 2015-05-02
    • 1970-01-01
    • 2018-08-17
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多