如何使用@FieldValue的ByteBuddy @Pipe注释来实现委托模式?

时间:2016-12-31 08:21:14

标签: java byte-buddy

使用ByteBuddy我正在尝试找到一种生成代理的有效方法,只需将所有方法调用转发到相同类型的基础委托实例,我就来了:How to implement a wrapper decorator in Java?,我试图实现建议的解决方案,但没有任何成功,表面上我粗略猜测而不太了解ByteBuddy的内部结构,看起来在检查匹配的委托时,可能会考虑下面的intercept方法的@FieldValue注释参数方法签名?我的用例有点复杂,但我写了一个简单的单元测试,重现了同样的问题,我使用的是ByteBuddy 1.5.13版本:

@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)

所以我想知道我是否正确使用@ Pipe / @ FieldValue注释,或者在使用ByteBuddy生成代理时是否有另一种委托方法调用的方法?提前谢谢!

2 个答案:

答案 0 :(得分:1)

使用appendParameterBinder代替defineParameterBinder解决了问题,TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTSMethodDelegation.to(TypeDescription typeDescription)指定的默认活页夹被覆盖,因此未定义@FieldValue活页夹我的错了。我每天都在使用ByteBuddy,我对该库的质量印象越来越深刻!

答案 1 :(得分:1)

对于转发,我实际上建议您使用MethodCall.invokeSelf().onField(delegate),其效果要比应用更复杂匹配的MethodDelegation好得多。请注意,可以将委派与MethodDelegation.to( ... ).andThen(MethodCall.invokeSelf().onField(delegate)

等委托结合使用

此外,我刚刚在即将发布的v1.6中更改了委托API,以避免您遇到的混乱并允许多项性能改进。

重要:如果这些方法是由另一个包中的类型而不是检测类型定义的,则无法管道受保护的方法。当前版本的Byte Buddy错过了这个检查,但它会从1.6.1开始抛出异常。您可以按not(isProtected())排除此类方法。 Object::clone是此类方法的典型候选者,因为它在java.包中定义。