2

すでにロードされているクラスのメソッドの戻り値を変更しようとしています。

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)

問題は、メソッドを追加していないということです。Byte Buddy は、上記のコードのどこにフィールドまたはメソッドを追加しますか?

編集:

public class StuffImpl {

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

public class Result {

    private String result;

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

1 に答える 1

2

new Result("intercepted")Byte Buddy がどこかに格納する必要がある固定値への委譲を定義します。実装はFixedValue、生成されたメソッドがそこから値を読み取ることができるように、静的フィールドを作成します。を回避することで、さまざまな方法でこれを回避できますFixedValue。たとえば、次のようになります。

  1. フィールドに値を保持する (参照 ID を保持する) 別のクラスに委譲します。

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

    これは最も普遍的なアプローチであり、もちろんnew Result("intercepted")メソッドから直接戻ることができます。

  2. インスタンスを動的に作成します。

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

    この場合、"intercepted"文字列はクラスの定数プールで参照できるため、フィールドに格納する必要はありません (プリミティブ値についても同様です)。


あなたStuffImplはおそらく静的初期化子を定義しています。この初期化子は、Byte Buddy によってprivateメソッドに分解され、追加のステートメントを追加できるようになります。

この動作を無効にするには、次のように設定します。

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

これは本当にドキュメントにあるはずで、次のリリースで追加します。

于 2016-01-29T08:16:17.750 に答える