Search code examples
javacode-generationbytecodeinstrumentationbyte-buddy

Intercept all methods/construtors/getters/setters from a specific namespace


I have a Java agent implemented like this:

public static void premain(String args, Instrumentation instrumentation) throws ClassNotFoundException {
    new AgentBuilder.Default()
      .type(isSubTypeOf(Object.class).and(nameStartsWith("com.my.domain")))
      .transform(new Transformer5())
      .installOn(instrumentation);
}

and then Transform class:

public class Transformer5 implements AgentBuilder.Transformer {
    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
        return builder.method(any().and(isDeclaredBy(typeDescription)))
                      .intercept(MethodDelegation.to(Interc4.class));
    }
}

and an intercepter:

public class Interc4 {

    static String indent = "";

    @RuntimeType
    @BindingPriority(BindingPriority.DEFAULT * 3)
    public static Object intercept(@SuperCall Callable<?> zuper, 
                                   @AllArguments Object[] allArguments, 
                                   @Origin String method) throws Exception {

        System.out.println(indent + "Intercepted4 M" + method);

        try {
            indent += "  ";
            return zuper.call();
        } finally {
            //post process
            indent = indent.substring(0, indent.length()-2);
        }

    }

}

The problem with this is that it doesn't intercept constructors and it also gives this kind of errors

Cannot define non-public or non-virtual method 'lambda$static$1' for interface type

What is the best way to make interceptor that will proxy each method in a class from some domain (I want to be able to get method name, inspect method arguments (if any) and just forward with execution).


Solution

  • There are two reasons for what is happening.

    1. Cannot define non-public or non-virtual method 'lambda$static$1' for interface type is caused by a validation bug in Byte Buddy. I am going to fix this for the next release. In the mean-time, you can disable validation by new AgentBuilder.Default(new ByteBuddy().with(TypeValidation.DISABLED)).

    2. You explicitly only intercept methods by matching via .method( ... ). You can intercept constructors by .constructor( ... ) or any invokable by .invokable( ... ). You will however not be able to use your interceptor for constructors where the super method call must be hard-coded. You can however split your interceptor in two bits:

      class Interceptor {
        public static void before() { ... }
        public static void after() { ... }
      }
      

      using

      .intercept(MethodDelegation.to(Interceptor.class).filter(named("before")
         .andThen(SuperMethodCall.INSTANCE
         .andThen(MethodDelegation.to(Interceptor.class).filter(named("after"))))
      

    This way, Byte Buddy can hard-code the super constructor invocation and yet trigger the before and after methods. Note that the @This annotation is not available to the before method.