2017-01-11 1 views
1

私は他の人からいくつかのByteBuddyコードを見ています。彼はByteBuddyを使用してランタイムサブクラスを生成し、実行時の管理コードを特定のオブジェクトに実装するためのプロキシとして使用されます。私は、私は生成されたクラスを再利用(及び機能のすべての呼び出しのためにそれを再生しない)ことができ、新たな定義されたフィールド_coreを使用したいラムダに直接coreオブジェクトを結合しないためにByteBuddy:クラスの構築中にインターセプトで新しい定義フィールドを使用します。

Class<? extends T> newSubClass = new ByteBuddy(ClassFileVersion.ofThisVm()) 
       .subclass(classType) 
       .defineField("_core", Object.class, Visibility.PUBLIC) //<--- 
       .method(ElementMatchers.isDeclaredBy(classType)) 
       .intercept(InvocationHandlerAdapter.of((proxy, method, m_args) -> { 
        //TODO: Need to replace core with _core as core is a function argument and will make it bound 
        return proxyHandler(core, method, m_args); //<-- 
       })) 
       .make() 
       .load(roleType.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
       .getLoaded(); 


T proxy = ReflectionHelper.newInstance(newSubClass, args); 
newSubClass.getField("_core").set(proxy, core); 

。 これを達成する方法はありますか?

ありがとうございます。

答えて

1

メソッドを定義するのと同じようにカスタムコンストラクタを定義できます。コンストラクタを定義する上で重要な点の1つは、最初の命令として別のコンストラクタ呼び出しが必要なことです。 MethodCall::invokeを使用してコンストラクタを呼び出すことができます。これはFieldAccessor::ofFieldと組み合わせることができます。

この方法では、次のようなクラスを定義することができます

new ByteBuddy(ClassFileVersion.ofThisVm()) 
    .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS) 
    .defineConstructor(Visibility.PUBLIC) 
    .withParameter(InvocationHandler.class) 
    .intercept(MethodCall.invoke(classType.getDeclaredConstructor()) 
    .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0))) 
    .defineField("_core", InvocationHandler.class, Visibility.PUBLIC) 
    .method(ElementMatchers.isDeclaredBy(classType)) 
    .intercept(InvocationHandlerAdapter.toField("_core")) 
    .make(); 

この方法では、インスタンスごとにカスタムInvocationHandlerを設定することができます。あなただけ_coreフィールドに状態を保存し、あなたのインターセプタからこのフィールドにアクセスしたい場合は、MethodDelegationを見て:あなたが必要な場合があります

new ByteBuddy(ClassFileVersion.ofThisVm()) 
    .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS) 
    .defineConstructor(Visibility.PUBLIC) 
    .withParameter(Object.class) 
    .intercept(MethodCall.invoke(classType.getDeclaredConstructor()) 
    .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0))) 
    .defineField("_core", Object.class, Visibility.PUBLIC) 
    .method(ElementMatchers.isDeclaredBy(classType)) 
    .intercept(MethodDelegation.to(MyHandler.class)) 
    .make(); 

public class MyHandler { 
    @RuntimeType 
    public static Object intercept(@FieldValue("_core") Object value) { ... } 
} 

その他の注釈を@This@AllArguments@Origin@SuperCallです。それほど必要がないほど、プロキシはより効率的になります。特に、@AllArgumentsは、割り当て要件のために高価である。

この場合、フィールドはスーパーコンストラクタコールの後にのみ設定されることに注意してください。また、スーパータイプにはデフォルトのコンストラクタがあると仮定します。あるいは、カスタムConstructorStrategyを実装することもできます。

キャッシュについては、Byte BuddyのTypeCacheをご覧ください。

+0

ヒントw.r.tをお寄せいただきありがとうございます。キャッシング。私は 'WeakHashMap 、Class >' elseを使ったでしょう。 – lschuetze

+1

値がキーのサブクラスであり、それを強く参照するため、これは機能しません。むしろ、これを回避する 'TypeCache'を使用して、値を弱く参照することもできます。 –

+0

'_core'に格納されている呼び出しハンドラに委譲したくないのですか?この場合、' MethodDelegation'実装を見てください。このクラスでは、 '@FieldValue(" _ core ")アノテーション私の更新された答えを見てください。 –

関連する問題