Как получить аннотации метода для прокси-класса? - PullRequest
2 голосов
/ 07 ноября 2019

У меня проблема при чтении аннотаций для методов прокси-класса.

В нем есть интерфейс, объект и аннотация, эта часть действительно проста:

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

И здесь возникает проблема: локальная переменная test имеет экземпляр класса Test, а локальная переменная 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

Я пытался найти решение, но этот вопрос касается извлечения аннотаций из аннотации (я полагаю), этоодин слишком о классе аннотации. Некоторые из них касаются других реализаций прокси.

➥ Итак, есть ли способ получить реальные аннотации от прокси-объекта или открыть класс, который скрыт под прокси (хотя хочешь прежний)?

1 Ответ

0 голосов
/ 07 ноября 2019

Итак, есть ли способ получить реальные аннотации от прокси-объекта или показать класс, который скрыт под прокси-сервером (хотя я хочу первый)?

Не напрямую, нет.

Идея дизайна 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();
    }
}

У вас есть конкретный экземплярс которым можно подумать (или какой-либо другой процесс, который вам нужно сделать).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...