【问题标题】:How to get method annotations on a proxied class?如何在代理类上获取方法注释?
【发布时间】:2019-11-07 14:17:00
【问题描述】:

我在读取代理类的方法注释时遇到问题。

方法上有接口、对象和注解,这部分真的很简单:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface A {
}

interface I {
    void method();
}

class Test implements I {
    @A
    public void method() { }
}

接下来,有一个 InvocationHandler 什么都不做,只是简单地调用带有传递参数的方法:

class DefaultInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(final Object o, final Method method, final Object[] args) throws Throwable {
        return method.invoke(o, args);
    }
}

还有一个main 方法可以打印Test 实例的声明方法及其代理副本:

class Main {
    public static void main(String[] args) {
        Object test = new Test();
        printMethods(test);         // Outputs that `I#method` has `A` annotation

        System.out.println();

        Object proxied = Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), new DefaultInvocationHandler());
        printMethods(proxied);      // Outputs that `I#method` does not have `A` annotation
    }

    static void printMethods(Object obj) {
        Arrays.stream(obj.getClass().getDeclaredMethods())
                .forEach(method -> System.out.println(method.toString() + " has A annotation: " + method.isAnnotationPresent(A.class)));
    }
}

问题来了:局部变量testTest类的一个实例,而局部变量proxied实际上是一个Proxy,所以它的方法上没有任何注解。这是程序的输出:

public void Test.method() has A annotation: true                // <- good thing

public final boolean $Proxy2.equals(java.lang.Object) has A annotation: false
public final java.lang.String $Proxy2.toString() has A annotation: false
public final void $Proxy2.method() has A annotation: false      // <-  bad thing
public final int $Proxy2.hashCode() has A annotation: false

我试过寻找解决方案,但this question 是关于从注释中提取注释(我想),this one 也是关于注释类的。 Some of them 是关于其他代理实现的。

➥ 那么,有没有办法从代理对象中获取实际注释,或者公开隐藏在代理下的类(不过我想要前一个)?

【问题讨论】:

  • 如果您错过了请注意,在method.invoke(o, args) 中,oProxy 实例。该调用将进入无限递归循环。
  • 代理为什么要有A注解?代理是I的实现,与Test类没有任何关系,那么为什么不相关类的注解会溢出到代理?
  • @Holger,是的,现在我明白了,谢谢。几个小时前,我认为ProxyTest 实例的包装器,但现在我看到它是InvocationHandler,它可能持有对代理对象的引用。在直接跳到示例之前,我应该阅读文档。 :D

标签: java reflection proxy


【解决方案1】:

那么,有没有办法从代理对象中获取实际注释, 或公开隐藏在代理下的类(我想要 前一个,但)?

不直接,不。

Proxy 设计背后的想法是,实际包装的实例(如果有的话)将隐藏在调用处理程序后面。您可以从newProxyInstance 方法中看到这一点:没有对传递到任何地方的test 实例的引用。 Proxy 实例不知道您的 Test 实例。

一种常见的模式是使用一个常见的InvocationHandler 子类,该子类保留对包装实例的引用并可以将其返回给您,您可以使用它来执行检查。例如,

abstract class InvocationHandlerWithTarget implements InvocationHandler {
    protected final Object target;

    public InvocationHandlerWithTarget(Object target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }
}

class DefaultInvocationHandler extends  InvocationHandlerWithTarget {
    public DefaultInvocationHandler(Object target) {
        super(target);
    }

    @Override
    public Object invoke(final Object o, final Method method, final Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}

然后检查您是否使用 Proxy 以及它的 InvocationHandler 是否符合您的预期

Object proxied = Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(),
            new DefaultInvocationHandler(test));

[...]

if (Proxy.isProxyClass(proxied.getClass())) {
    var handler = Proxy.getInvocationHandler(proxied);
    if (handler instanceof InvocationHandlerWithTarget) {
        var handlerWithTarget = (InvocationHandlerWithTarget) handler;

        // now process the target
        handlerWithTarget.getTarget();
    }
}

然后你就有了一个具体的实例来反映(或你需要做的任何其他处理)。

【讨论】:

  • 感谢您的精彩回答!原来我以错误的方式使用代理。现在您已经指出代理实例中没有对test 的引用,这看起来很明显,但是当我阅读示例并编写这段代码时,我完全没有注意到这一事实。 :/再次感谢您的解释! :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
相关资源
最近更新 更多