使用 ByteBuddy 拦截未实现的接口方法

Intercepting unimplemented interface method with ByteBuddy

让我们有一个简单的界面:

public interface Person {

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

现在我这样创建代理:

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;
        }

    }

}

当我执行测试时,我看到这个输出:

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

如果调用方法源自接口并且不是由 superclass 实现,我似乎永远无法访问被调用方法的方法签名?!

此外,我希望当我调用 theInstance.setFirstName("ABC"); 时,我会看到 Intercept D: 也接受所有输入参数。

我在这里想念什么?我阅读了文档,但我仍然感到困惑。

一般来说,我想创建 Delegator 来捕获代理 class 中的所有方法并处理以下情况:

为此,我需要访问:

如何实现?

Auch - 问题是某些注释在不同包的 ByteBuddy 中声明了两次 - 例如:

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

当使用来自 implementation 包的注解时,一切正常。这是一个令人讨厌的陷阱!