javabyte-buddy

Unable to obtain Method in Byte Buddy Interceptor


When I add:

@Origin Method method

to my interceptor:

@RuntimeType
  public static void intercept(
      @Origin Method method, @RuntimeType @AllArguments Object[] allArguments) {
    System.out.println("intercepted with args: " + Arrays.toString(allArguments));
    System.out.println("intercepted method: " + method);
  }

I end up with an exception:

Error invoking java.lang.instrument.Instrumentation#retransformClasses
class redefinition failed: attempted to change the schema (add/remove fields)

My setup looks like this:

ByteBuddyAgent.install();
    final ByteBuddy byteBuddy = new ByteBuddy();

    byteBuddy
        .redefine(ByteBuddyFoo.class)
        .method(ElementMatchers.named("anotherMethod").or(ElementMatchers.named("toString")))
        .intercept(MethodDelegation.to(MethodInterceptor.class))
        .make()
        .load(ByteBuddyFoo.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

And my simple POJO is:

public class ByteBuddyFoo {
  @Override
  public String toString() {
    return "ByteBuddyFoo{}";
  }

  public void anotherMethod(final String input) {
    System.out.println("input from anotherMethod: " + input);
  }
}

When my interceptor doesn't have @Origin Method method in it, it intercepts calls just fine; however, as soon as I add that back, it throws the above exception.

I'd like to be able to intercept any method call that I'd like and have access to both the Method, its arguments, and result. Originally, I was doing this through AspectJ; however, I want to be able to do this at runtime so my code doesn't need to be modified.


Solution

  • You'd need to disable the caching of the Method instance as with:

    @Origin(cache = false)
    

    for this, but this makes the call rather expensive. If you just want to stringify the method instance to begin with rather use @Origin String method which does not need caching anyway

    When redefining, you should use .disableClassFormatChanges().