我正在尝试更改已加载类的方法的返回值。

从ByteBuddy的文档(http://bytebuddy.net/#/tutorial)中看来,只要我不添加任何字段/方法,就可以使用Java代理来实现。

我的代码如下:

ByteBuddyAgent.install();

new ByteBuddy()
        .redefine(StuffImpl.class)
        .method(returns(Result.class))
        .intercept(FixedValue.value(new Result("intercepted")))
        .make()
        .load(StuffImpl.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());


但是我收到以下异常:

java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)


问题是,我没有添加任何方法。字节好友在上面的代码中的哪里添加字段或方法?

编辑:

public class StuffImpl {

    public Result validate() {
        return new Result("original");
    }
}

public class Result {

    private String result;

    public Result(String result) {
        this.result = result;
    }
}

最佳答案

您将委派定义为Byte Buddy需要存储在某个位置的固定值new Result("intercepted")FixedValue实现为您创建一个静态字段,以便生成的方法可以从中读取您的值。您可以通过避免FixedValue来以不同的方式解决这个问题,例如:


委托给另一个将值保存在字段中的类(保留引用标识)。

MethodDelegation.to(Holder.class);
class Holder {
  static Result result = new Result("intercepted");
  static Result intercept() { return result; }
}


这是最通用的方法,您当然可以直接从该方法返回new Result("intercepted")
动态创建实例:

MethodCall.construct(Result.class.getDeclaredConstructor(String.class))
          .with("intercepted");


在这种情况下,"intercepted"字符串不需要存储在字段中,因为可以在类的常量池中对其进行引用(原始值也是如此)。




您的StuffImpl可能定义了一个静态初始化器。字节伙伴将这个初始化程序分解为private方法,以便它可以向其添加其他语句。

您可以通过设置禁用此行为:

new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);


这确实应该在文档中,我将在下一个版本中添加它。

10-01 12:41