2017-07-05 8 views
2

ByteBuddyを使用すると、別のインスタンスを呼び出して結果を変換して1つのインスタンスメソッドを実装できますか?例えばByteBuddyを使用したチェーン/トランスフォームメソッドの呼び出し

(玩具例):

上述した
public abstract class Foo { 
    public String bar() { 
    return "bar"; 
    } 

    public abstract int baz(); 
} 

、私はそれがbar()を呼び出し、返された文字列の長さを返しbazように実装することができますか?それはあたかも、すなわち:

public int baz() { 
    return bar().length(); 
} 

単純に、私は次のことを試してみました:

Method bar = Foo.class.getDeclaredMethod("bar"); 
Method baz = Foo.class.getDeclaredMethod("baz"); 

Method length = String.class.getDeclaredMethod("length"); 

Foo foo = new ByteBuddy() 
    .subclass(Foo.class) 
    .method(ElementMatchers.is(baz)) 
    .intercept(
    MethodCall.invoke(bar)     // call bar()... 
     .andThen(MethodCall.invoke(length)) // ... .length()? 
).make() 
    .load(Foo.class.getClassLoader()) 
    .getLoaded() 
    .newInstance(); 

System.out.println(foo.baz()); 

私はandThen()は、最初の呼び出しの戻り値に呼び出された考えで間違っていたようにしかし、それが見えます。生成されたインスタンスで呼び出されたようです。

Exception in thread "main" java.lang.IllegalStateException: 
    Cannot invoke public int java.lang.String.length() on class Foo$ByteBuddy$sVgjXXp9 
    at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForContextualInvocation 
    .invoke(MethodCall.java:1667) 

Iはまた、インターセプタを試みた:

class BazInterceptor { 
    public static int barLength(@This Foo foo) { 
    String bar = foo.bar(); 
    return bar.length(); 
    } 
} 

で:

Foo foo = new ByteBuddy() 
    .subclass(Foo.class) 
    .method(ElementMatchers.is(baz)) 
    .intercept(MethodDelegation.to(new BazInterceptor())) 
    // ...etc. 

これは実行が、無意味な結果870698190を生じ、そしてbarLength()にブレークポイントを設定および/または印刷ステートメントを追加それは決して呼ばれていないことを示唆した。あまりにも明確に私は迎撃者または@Thisを正しく理解していません。

ByteBuddyに1つのメソッドを呼び出し、その戻り値で別のメソッドを呼び出す方法はありますか?


k5_'s answerパー:BazInterceptor作品であればいずれか

  • 我々は、上記のように、new BazInterceptor()に委任しますが、インスタンスメソッド、またはbarLength()ます
  • たちはクラスメソッドbarLength()ままにし、インスタンスではなくBazInterceptor.classに委譲します。

私は実際にチェックしていなかったけれども、私は、870698190BazInterceptorインスタンスのhashCode()に委任した疑い。

答えて

1

インスタンスをインターセプタとして使用すると、インスタンスメソッドが優先されます(静的メソッドはまったく受け入れられないことがあります)。 int baz()メソッドのシグネチャに一致するインスタンスメソッドがあります。int hashCode()です。取得する数字はnew BazInterceptor()インスタンスのハッシュコードです。私が知っ

オプション:

  • barLengthから、それは実際に迎撃のために使用されるそのようにstaticを削除します。
  • あなたがBazInterceptorインスタンスの任意のフィールド/状態を使用していないとして、私は2番目のオプションを好むだろうインターセプター.intercept(MethodDelegation.to(BazInterceptor.class))

としてクラスを追加します。

+0

ありがとうございました。この場合は2番目のオプションを使用しますが、最初のオプションがインターセプタインスタンスがある状態を持つ必要がある状況でも機能することを知っておくとよいでしょう。 –

1

現在、Byte Buddyでは良い方法はありませんが、これは簡単に追加できます。あなたはtrack the progress on GitHubです。私はしばらくするとそれを追加します。

このような連鎖呼び出しを今日実装する場合は、それらをJavaコードで実装し、Adviceコンポーネントを使用してこのコードをインライン化できます。あるいは、引数を手動でロードする必要があるMethodInvocationインスタンスに基づいて独自のByteCodeAppenderを作成することで、より明示的にバイトコードを記述することができます。

関連する問題