Этого вопроса не было бы, если бы 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. АОП в нем реализован с помощью 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 Method, который представляет точный метод, который был перехвачен?
Я предполагаю, что один из способов - сделать это методом грубой силы: получить суперклассы/интерфейсы аргументов и попытаться получить метод, используя эти типы, пока вы больше не получите NoSuchMethodException, но это просто уродливо. Есть ли нормальный чистый способ сделать это?