Novoj
Novoj

Reputation: 341

Intercepting unimplemented interface method with ByteBuddy

Let's have simple interface:

public interface Person {

    String getFirstName();
    void setFirstName(String firstName);
    
}

Now I create proxy this way:

public class TestClass {

    @Test
    public void testMethod() throws InstantiationException, IllegalAccessException {
        final Class<?> proxy = new ByteBuddy().subclass(Object.class).implement(Person.class)
            // AND INTERCEPTS ALL ABSTRACT AND OBJECT DEFAULT METHODS
            // AND TRAP ALL METHODS EXCEPT CONSTRUCTORS AND FINALIZER
            .method(
                ElementMatchers.isAbstract()
            )
            // AND DELEGATE CALL TO OUR INVOCATION HANDLER STORED IN PRIVATE FIELD OF THE CLASS
            .intercept(MethodDelegation.to(Delegator.class))
            // NOW CREATE THE BYTE-CODE
            .make()
            // AND LOAD IT IN CURRENT CLASSLOADER
            .load(TestClass.class.getClassLoader())
            // RETURN
            .getLoaded();

        final Person theInstance = (Person) proxy.newInstance();
        theInstance.setFirstName("ABC");
        theInstance.getFirstName();
    }

    public static class Delegator {

        @RuntimeType
        public static Object intercept(@This Object proxy, @Origin Method method, @AllArguments Object[] args) throws Throwable {
            System.out.println("Intercept A: " + proxy + ", " + method + ", " + args);
            return null;
        }

        @RuntimeType
        public static Object intercept(@This Object proxy, @Origin Method method) throws Throwable {
            System.out.println("Intercept B: " + proxy + ", " + method);
            return null;
        }

        @RuntimeType
        public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod) throws Throwable {
            System.out.println("Intercept C: " + proxy + ", " + defaultMethod + ", " + superMethod);
            return null;
        }

        @RuntimeType
        public static Object interceptE(@This Object proxy, @DefaultMethod(nullIfImpossible = true) Method defaultMethod, @SuperMethod(nullIfImpossible = true) Method superMethod, @AllArguments Object[] args) throws Throwable {
            System.out.println("Intercept D: " + proxy + ", " + defaultMethod + ", " + superMethod + ", " + args);
            return null;
        }

    }

}

When I execute the test I see this output:

Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null
Intercept C: net.bytebuddy.renamed.java.lang.Object$ByteBuddy$MVjuc1JA@147ed70f, null, null

It seems that I cannot ever access method signature of the called method if it originates in interface and is not implemented by the superclass?!

Also I would expect that when I call theInstance.setFirstName("ABC"); I would see Intercept D: which also accepts all input arguments.

What do I miss here? I read the documentation but I'm still confused.

In general I want to create Delegator that catches all methods in proxied class and handles following situtations:

For this I would need access to the:

How do I achieve that?

Upvotes: 1

Views: 500

Answers (1)

Novoj
Novoj

Reputation: 341

Auch - the problem is that some annotations are declared twice in ByteBuddy in different packages - for example:

net.bytebuddy.implementation.bind.annotation.AllArguments
net.bytebuddy.asm.Advice.AllArguments

When annotation from the implementation package is used, everything works. That's a nasty catch!

Upvotes: 2

Related Questions