2016-10-27 9 views
2

Javaでは、InvocationHandlerの実装を使用して動的プロキシを作成することができます。 JVMの最適化にもかかわらず、リフレクションの使用には常にメソッドを呼び出すオーバーヘッドがあります。ByteBuddyを使用して動的プロキシを作成する方法

この問題を解決しようとすると、実行時にByteBuddyを使用してプロキシクラスを作成しようとしましたが、ドキュメントがこの面で十分にはっきりしていないようです。

メソッドの呼び出しをいくつかのクラスインスタンスに転送するために、MethodCallProxyを作成するにはどうすればよいですか?

編集:私はRPCシステムを構築してい

より良い私の問題を明確にするためには、私は私が達成したいものの例を提供しています。メソッド呼び出しの各側で、私は、(両方の呼び出し元/呼び出し先がJVMの下で実行されている)契約を定義するインタフェースを持っています。

@Contract 
interface ISomeService { 
    fun someMethod(arg0: String, arg1: SomePojo): PojoResult 
} 

コールサイトでは、すべてのメソッド呼び出しをインターセプトして呼び出し先に転送するプロキシを挿入します。

ByteBuddy() 
    .subclass(Any::class.java) 
    .implement(serviceClass) 

    // Service contract method delegation 
    .method(isDeclaredBy(serviceClass)).intercept(
     MethodDelegation 
      .to(ServiceProxyInterceptor()) 
      .filter(not(isDeclaredBy(Any::class.java))) 
    ) 

    .make() 
    .load(this) 
    .loaded as Class<T> 

そして、最後に、呼び出し先で、私は、呼び出しパラメーターを非整列化し、サービスの実装に転送するための責任、いくつかのハンドラ、各サービスのメソッドのいずれかを持っています。

@Service 
class SomeServiceImpl { 
    fun someMethod(arg0: String, arg1: SomePojo): PojoResult { 
     // ... 
    } 
} 

私は、コード生成を使用してこの問題を解決することができますが、結果としてjarファイルが非常に大きくなることができます。したがって、これらのハンドラの汎用バージョンを作成し、それぞれのメソッド呼び出しをすべてのメソッド呼び出しを傍受してISomeServiceに接続し、SomeServiceImplに転送する必要があります。

答えて

3

Byte Buddyにプロキシクラスを作成する方法はたくさんあります。正確な方法は、ユースケースによって異なります。最も簡単な方法はInvocationHandlerAdapterです。あなたはSomeClassのプロキシを作成することを考えると、あなたが使用して作成することができます:あなたは別のインスタンスにデリゲートとプロキシを作成したい場合は

Class<? extends SomeClass> proxy = new ByteBuddy() 
    .subclass(SomeClass.class) 
    .method(ElementMatchers.any()) 
    .intercept(InvocationHandlerAdapter.of(invocationHandler)) 
    .make() 
    .load(SomeClass.class.getClassLoader()); 

、あなたは、さらにフィールドを定義します。これは、次の手順で行うことができます。

Class<? extends SomeClass> proxy = new ByteBuddy() 
    .subclass(SomeClass.class) 
    .defineField("handler", InvocationHandler.class, Visibility.PUBLIC) 
    .method(ElementMatchers.any()) 
    .intercept(InvocationHandlerAdapter.toField("handler")) 
    .make() 
    .load(SomeClass.class.getClassLoader()); 

あなたは反射を介して、または、例えばなどセッターインタフェースを実装することにより、上記のフィールドを設定します:

interface HandlerSetter { 
    InvocationHandler getHandler(); 
    void setHandler(InvocationHandler handler); 
} 

Class<? extends SomeClass> proxy = new ByteBuddy() 
    .subclass(SomeClass.class) 
    .defineField("handler", InvocationHandler.class, Visibility.PUBLIC) 
    .implement(HandlerSetter.class) 
    .intercept(FieldAccessor.ofField("handler")) 
    .method(ElementMatchers.any()) 
    .intercept(InvocationHandlerAdapter.toField("handler")) 
    .make() 
    .load(SomeClass.class.getClassLoader()); 

あなたは今、クラスとキャストをインスタンス化することができますハンドラを設定するためのインタフェースへのクラス

InvocationHandler以外にも、プロキシを作成する他の多くの方法があります。 1つの方法は、柔軟性が高く、しばしば高速で、オンデマンドでスーパーメソッドを呼び出すことができるMethodDelegationを使用することです。転送インスツルメンテーションは、MethodCallまたはForwarding計測器を使用して適用することもできます。詳細な情報は、それぞれのクラスjavadocで見つけることができます。

+0

もちろん、この質問の特定の部分に対処するために、InvokeHandlerを呼び出すByteBuddy生成プロキシがJREが 'java.lang.reflect.Proxy'を生成したより効率的であると仮定する理由はありません。 InvocationHandler' ... – Holger

+0

違いは主にJVMがインターフェイスのみをサポートしているのに対して、Byte Buddyは計測器クラスもサポートしている点です。パフォーマンス面では、リフレクションに比べてコード生成のメリットがあると思います。後者は疑わしいです。ほとんどの場合、私はリフレクション・インフレーションが十分であると思っています。 –

+0

私はByteBuddyとReflectionの機能の違いを知っていますが、問題はこれがまったく問題ではないことを示しています。唯一言及された動機はReflectionの主張されたオーバーヘッドです。パフォーマンスが重要なシナリオがあるかもしれませんが、 'InvocationHandler'に固執すると、コード生成は間違った終わりに対処しています。たとえば、プリミティブ型のボクシングと引数を配列に埋め込むことは... – Holger

関連する問題