2016-12-31 287 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,我对该库的质量越来越印象深刻!