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).
There are two reasons for what is happening.
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))
.
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.