I'm trying to use byte buddy to intercept methods but have found that it only appears to work when you sub-type an interface.
The code snippet below demonstrates this, you can see "hello" being passed into the same "message" method of the 3 types but only the Interface subtype is intercepted.
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.attribute.AnnotationRetention;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
class Sandbox {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
testIt(StaticClass.class).message("hello");
testIt(AbstractStaticClass.class).message("hello");
testIt(Interface.class).message("hello");
}
private static <T> T testIt(Class<T> aClass) throws IllegalAccessException, InstantiationException {
return new ByteBuddy().with(AnnotationRetention.ENABLED)
.subclass(aClass)
.method(
ElementMatchers.isAnnotatedWith(AnAnnotation.class)
).intercept(
MethodDelegation.withDefaultConfiguration()
.withBinders(Morph.Binder.install(Invoker.class))
.to(new Interceptor()))
.make()
.load(Sandbox.class.getClassLoader())
.getLoaded()
.newInstance();
}
public interface Invoker {
Object invoke(Object[] args);
}
public static class Interceptor {
@RuntimeType
public Object intercept(@This Object proxy, @Origin Method method, @Morph(defaultMethod = true) Invoker invoker, @AllArguments Object[] args) {
return invoker.invoke(new Object[]{"there"});
}
}
public static class StaticClass {
@AnAnnotation
String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
public static abstract class AbstractStaticClass {
@AnAnnotation
String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
public interface Interface {
@AnAnnotation
default String message(String message) {
System.out.println(getClass().getName() + ": message: " + message);
return message;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
private @interface AnAnnotation {}
}
I also found that if I changed the classes to inherit each other, like below, then the method doesn't execute at all for the AbstractStaticClass and StaticClass.
public static class StaticClass extends AbstractStaticClass { ... }
public static abstract class AbstractStaticClass implements Interface { ... }
public interface Interface { ... }
How can I intercept the methods of classes as well of interfaces?
Thank you