bytebuddy能否拦截一个方法调用,让它调用第二个对象的私有方法?
Can bytebuddy intercept a method call and make it call a private method of a second object?
给定一个 class A
使用 public 方法 overrideMe(B that, C arg)
和第二个 class B
使用私有方法 callMe(C arg)
。是否可以使用 bytebuddy 生成一个子类,该子类将从已实现的 overrideMe(B that, C arg)
中调用私有方法 callMe(C arg)
?这基本上会模仿 JVM 反射 GeneratedMethodAccessor,它正是这样做的。
目前,我正在尝试使用这样的 MethodCall:
Method callMeHandler = B.class.getDeclaredMethod("callMe");
callMeHandler.setAccessible(true);
MethodCall methodCall = MethodCall.invoke(callMeHandler).onArgument(0);
if(callMeHandler.getParameterCount() == 1)
methodCall = methodCall.withArgument(1);
Composable coercedMethodCall = methodCall
.withAssigner(ReferenceTypeAwareAssigner.INSTANCE, Typing.DYNAMIC);
String className = callMeHandler.getDeclaringClass().getName()
+ "$_generatedAccessor$" + callMeHandler.getName()
+ "$" + accessorCounter.getAndIncrement();
new ByteBuddy().subclass(GeneratableHandlerInvocation.class)
.name(className)
.method(ElementMatchers.named("invoke"))
.intercept(Advice.to(GeneratableHandlerInvocation.class)
.wrap(coercedMethodCall))
.make()
.load(callMeHandler.getDeclaringClass().getClassLoader())
.getLoaded()
.newInstance();
,但它抱怨
java.lang.IllegalStateException: Cannot invoke private void com.mycompany.secret.B.callMe() virtually
at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForVirtualInvocation$WithImplicitType.invoke(MethodCall.java:2124)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2400)
at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7156)
at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7109)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:614)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:603)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:521)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4102)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1612)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:174)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2662)
at com.mycompany.secret.Main.defineTheSpecialClass(Main.java:190)
不,这不可能。 JVM 的字节码验证器禁止它。所以,字节好友一开始就不允许你创建这样的方法调用。
如果您想更改 class 并允许这样的调用,请查看 Byte Buddy 的代理 API 和 Advice
class,它允许您更改现有的代码。
给定一个 class A
使用 public 方法 overrideMe(B that, C arg)
和第二个 class B
使用私有方法 callMe(C arg)
。是否可以使用 bytebuddy 生成一个子类,该子类将从已实现的 overrideMe(B that, C arg)
中调用私有方法 callMe(C arg)
?这基本上会模仿 JVM 反射 GeneratedMethodAccessor,它正是这样做的。
目前,我正在尝试使用这样的 MethodCall:
Method callMeHandler = B.class.getDeclaredMethod("callMe");
callMeHandler.setAccessible(true);
MethodCall methodCall = MethodCall.invoke(callMeHandler).onArgument(0);
if(callMeHandler.getParameterCount() == 1)
methodCall = methodCall.withArgument(1);
Composable coercedMethodCall = methodCall
.withAssigner(ReferenceTypeAwareAssigner.INSTANCE, Typing.DYNAMIC);
String className = callMeHandler.getDeclaringClass().getName()
+ "$_generatedAccessor$" + callMeHandler.getName()
+ "$" + accessorCounter.getAndIncrement();
new ByteBuddy().subclass(GeneratableHandlerInvocation.class)
.name(className)
.method(ElementMatchers.named("invoke"))
.intercept(Advice.to(GeneratableHandlerInvocation.class)
.wrap(coercedMethodCall))
.make()
.load(callMeHandler.getDeclaringClass().getClassLoader())
.getLoaded()
.newInstance();
,但它抱怨
java.lang.IllegalStateException: Cannot invoke private void com.mycompany.secret.B.callMe() virtually
at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForVirtualInvocation$WithImplicitType.invoke(MethodCall.java:2124)
at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2400)
at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7156)
at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7109)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:614)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:603)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:521)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4102)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1612)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:174)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2662)
at com.mycompany.secret.Main.defineTheSpecialClass(Main.java:190)
不,这不可能。 JVM 的字节码验证器禁止它。所以,字节好友一开始就不允许你创建这样的方法调用。
如果您想更改 class 并允许这样的调用,请查看 Byte Buddy 的代理 API 和 Advice
class,它允许您更改现有的代码。