Получение шаблона / универсального объекта java.lang.reflect.Method из org.aspectj.lang.ProceedingJoinPoint - PullRequest
2 голосов
/ 06 марта 2011

Этот вопрос не существовал бы, если бы AspectJ работал так же, как работают перехватчики EJB.

Рассмотрим базовый сценарий EJB-перехватчиком:

@AroundInvoke
public Object log(final InvocationContext ctx) throws Exception {
    // do stuff before
    final Object result = ctx.proceed();
    // do stuff after
    return result;
}

Теперь мне нужен объект Method, который перехватывается. Здесь я могу просто сделать это:

        Method method = ctx.getMethod();

И все, после этого я буду проверять аннотации перехваченного метода.

Теперь я работаю с приложением, которое развернуто в контейнере сервлетов (Tomcat) и, следовательно, не имеет EJB. AOP в нем реализован с использованием Spring + AspectJ.

Метод округления выглядит следующим образом:

@Around("execution(* foo.bar.domain.integration.database.dao..*(..))")
public Object log(final ProceedingJoinPoint pjp) throws Throwable {
    // do stuff before
    final Object result = pjp.proceed();
    // do stuff after
    return result;
}

Здесь я больше не могу сделать это:

Method method = pjp.getMethod(); // no such method exists

Вместо этого я вынужден сам получить объект Method, используя отражение следующим образом:

    final String methodName = pjp.getSignature().getName();
    final Object[] arguments = pjp.getArgs();
    final Class[] argumentTypes = getArgumentTypes(arguments);
    final Method method = pjp.getTarget().getClass().getMethod(methodName, argumentTypes);

Это работает, пока вы не захотите овладеть методом шаблона:

@Transactional
public <T extends Identifiable> T save(T t) {
    if (null == t.getId()) {
        create(t);
        return t;
    }
    return update(t);
}

Предположим, вы вызываете этот метод следующим образом:

Person person = new Person("Oleg");
personService.save(person);

Вы получите:

Caused by: java.lang.NoSuchMethodException: foo.bar.domain.integration.database.dao.EntityService.save(foo.bar.domain.entity.Person)

который выбрасывается:

pjp.getTarget().getClass().getMethod()

Проблема в том, что во время выполнения универсальных шаблонов не существует, а фактическая сигнатура метода:

public Identifiable save(Identifiable t) {}

final Class [] argumentsTypes будет содержать один элемент, а его тип будет Person. Итак, этот звонок:

pjp.getTarget().getClass().getMethod(methodName, argumentTypes);

будет искать метод:

save(Person p)

и не найдет его.

Итак, вопрос в том, как мне получить экземпляр java template Метод объекта, представляющий точный метод, который был перехвачен?

Полагаю, один из способов - сделать это методом грубой силы: получите аргументы суперклассы / интерфейсы и попробуйте получить метод, использующий эти типы, пока вы больше не получите исключение NoSuchMethodException, но это выглядит ужасно. Есть ли нормальный чистый способ, которым я могу это сделать?

Ответы [ 2 ]

4 голосов
/ 09 марта 2011

Хорошо, теперь моя проблема решена.После более внимательного изучения того, что: methodSignature.getMethod () возвращает, я заметил, что он возвращает интерфейс вместо реализующего класса, и, конечно, на интерфейсе не было никаких аннотаций.Это отличается от EJB-перехватчиков, где getMethod () возвращает метод реализующего класса.

Итак, окончательное решение таково:

    final String methodName = pjp.getSignature().getName();
    final MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
    Method method = methodSignature.getMethod();
    if (method.getDeclaringClass().isInterface()) {
        method = pjp.getTarget().getClass().getDeclaredMethod(methodName, method.getParameterTypes());    
    }

и, если хотите, вы можете обработать аннотации интерфейса здесь какну, если нужно.

Также обратите внимание на этот бит: method.getParameterTypes () без этого он все равно будет генерировать NoSuchMethodException, поэтому хорошо, что мы можем получить правильную подпись через ((MethodSignature) pjp.getSignature ()).getMethod ();

Надеюсь, больше никаких сюрпризов, хотя я не доволен использованием отражения здесь, я бы просто предпочел иметь экземпляр метода реализующего класса, как в InvocationContext EJB.

Кстати, родной подход Spring без AspectJ:

public Object invoke(final MethodInvocation invocation) throws Throwable {}

возвращает интерфейс и не реализует класс.Проверено и ради знаний.

С наилучшими пожеланиями и спасибо за помощь, я действительно ценю это.Олег

2 голосов
/ 07 марта 2011
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();

Это не сразу очевидно, в этом виноват API.

Также см. Spring AOP: как получить аннотации рекомендованного метода . Я не проверял это сам. ОП говорит, что решил проблему для него. Для другого использования потребовалась дополнительная аннотация @Around(annotation...). (Попробуйте установить цель, например, только METHOD и посмотрите, как она себя ведет)

...