2016-12-31 282 views
1

隨着ByteBuddy我試圖找到一種有效的方式來生成一個代理,僅僅轉發所有方法調用同一類型的基礎委託實例,我來到這個防空火炮:How to implement a wrapper decorator in Java?,我試圖執行建議的解決方案,但沒有任何成功,表面上我粗略猜測,對ByteBuddy的內部知道不多的時候,它看起來像檢查匹配委託的方法簽名時,可能會考慮下面的intercept方法的@FieldValue註釋參數嗎?我的使用情況更加複雜了一點,但我寫了一個簡單的單元測試重現同樣的問題,我使用ByteBuddy版本1.5.13:如何使用ByteBuddy @Pipe註解與@FieldValue實現委託模式?

@Test 
public void testDelegate() throws Exception { 

    Object delegate = "aaa"; 

    Class<?> delegateClass = new ByteBuddy().subclass(Object.class) 
      .method(ElementMatchers.any()).intercept(MethodDelegation.to(Interceptor.class).defineParameterBinder(Pipe.Binder.install(Function.class))) 
      .defineField("delegate", Object.class, Modifier.PUBLIC) 
      .make() 
      .load(getClass().getClassLoader()) 
      .getLoaded(); 

    Object obj = delegateClass.newInstance(); 
    delegateClass.getField("delegate").set(obj, delegate); 

    assertThat(obj, equalTo("aaa")); 
} 

這個攔截器工作正常和單元測試順利過關:

public static class Interceptor { 

    @RuntimeType 
    public static Object intercept(@Pipe Function<Object, Object> pipe) { 
     return pipe.apply("aaa"); 
    } 
} 

但是,如果我這一個替換上面的攔截,並嘗試以@FieldValue注入委託場:

public static class Interceptor { 

    @RuntimeType 
    public static Object intercept(@Pipe Function<Object, Object> pipe, @FieldValue("delegate") Object delegate) { 
     return pipe.apply(delegate); 
    } 
} 

我得到以下錯誤:

java.lang.IllegalArgumentException: None of [public static java.lang.Object io.github.pellse.decorator.DecoratorTest$Interceptor.intercept(java.util.function.Function,java.lang.Object)] allows for delegation from public boolean java.lang.Object.equals(java.lang.Object) 
at net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor.process(MethodDelegationBinder.java:881) 
at net.bytebuddy.implementation.MethodDelegation$Appender.apply(MethodDelegation.java:1278) 
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:678) 
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:667) 
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:586) 
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4305) 
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1796) 
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:172) 
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:153) 
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2568) 
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2670) 
at io.github.pellse.decorator.DecoratorTest.testDelegate(DecoratorTest.java:476) 

所以我想知道如果我正確地使用@管/ @ fieldValue方法註釋或是否有另一種方式的委託方法與ByteBuddy生成代理調用時?提前致謝!

回答

1

對於轉發,我實際上建議您使用MethodCall.invokeSelf().onField(delegate),它比應用更復雜匹配的MethodDelegation表現更好。請注意,代表團可以與代表團合併,如MethodDelegation.to(...).andThen(MethodCall.invokeSelf().onField(delegate)

而且,我只是改變了代表團API在即將到來的V1.6,以避免您遇到的困惑和允許的性能改進。

重要:如果這些方法是由另一個軟件包中的類型定義的,則不可能對受保護的方法進行管道管理。 Byte Buddy的當前版本錯過了此項檢查,但它會從1.6.1開始引發異常。您可以通過not(isProtected())排除此類方法。 Object::clone是在java.包中定義的這種方法的典型候選者。

+0

我的使用情況下,我的項目加上「攔截(MethodCall.invokeSelf()。onField(代表).withAllArguments())」,非常感謝指導的偉大工程! –

+0

當我嘗試'invokeSelf()onField()'上面的單元測試,我得到: 'java.lang.VerifyError的:在invokevirtual 異常詳細信息受保護的數據錯誤訪問: 位置: 網/ bytebuddy /改名/java/lang/Object$ByteBuddy$POYl6ppy.clone()Ljava/lang/Object; @ 4:invokevirtual 原因: 類型 '的java /郎/對象'(當前幀,堆棧[0])是不能分配給淨/ bytebuddy /重命名/爪哇/郎/對象$ $ ByteBuddy POYl6ppy'' \t 當我調用delegateClass時。newInstance(),如果我過濾出'clone()'方法,一切正常,委託給一個受保護的方法不會有多大意義 –

+0

我更新了Byte Buddy以引發一個意外的異常。看到我更新的答案。 –

1

使用appendParameterBinder代替defineParameterBinder固定的問題,如TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTSMethodDelegation.to(TypeDescription typeDescription)指定的默認結合物覆蓋,因此@FieldValue粘結劑不再定義,我的錯誤。每天我都在使用ByteBuddy,我對該庫的質量越來越印象深刻!