如何使用ByteBuddy @Pipe 注解和@FieldValue 实现委托模式?
How to use ByteBuddy @Pipe annotation with @FieldValue to implement delegate pattern?
通过 ByteBuddy,我试图找到一种生成代理的有效方法,该代理只需将所有方法调用转发给相同类型的底层委托实例,我遇到了这个:,我试图实施建议的解决方案但没有任何成功,表面上我粗略的猜测是在不了解 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 生成代理时是否有另一种方法来委托方法调用?提前致谢!
使用 appendParameterBinder
而不是 defineParameterBinder
解决了这个问题,TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTS
在 MethodDelegation.to(TypeDescription typeDescription)
中指定的默认活页夹被覆盖,因此 @FieldValue
活页夹被不再定义,我的错误。我每天都在使用 ByteBuddy,那个库的质量给我留下了越来越深刻的印象!
对于转发,我实际上建议您使用 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.
包中定义的这种方法的典型候选者。
通过 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 生成代理时是否有另一种方法来委托方法调用?提前致谢!
使用 appendParameterBinder
而不是 defineParameterBinder
解决了这个问题,TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTS
在 MethodDelegation.to(TypeDescription typeDescription)
中指定的默认活页夹被覆盖,因此 @FieldValue
活页夹被不再定义,我的错误。我每天都在使用 ByteBuddy,那个库的质量给我留下了越来越深刻的印象!
对于转发,我实际上建议您使用 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.
包中定义的这种方法的典型候选者。