如果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();
就是这样,在此之后我将检查截获的方法的注释。
现在我正在使用部署到servlet容器(Tomcat)的应用程序,因此没有EJB。其中的AOP是使用Spring + AspectJ实现的。
around方法如下所示:
@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 [] argumentTypes将包含一个元素,其类型将为Person。所以这个电话:
pjp.getTarget().getClass().getMethod(methodName, argumentTypes);
将查找方法:
save(Person p)
并且找不到它。
所以问题是:如何获取java的模板方法对象的实例,该对象代表截获的确切方法?
我想其中一种方法是以蛮力的方式做到这一点: 获取参数超类/接口并尝试使用这些类型获取方法,直到您不再获得NoSuchMethodException,但这很简单。我可以做到这一点吗?
答案 0 :(得分:4)
好的,我现在已经解决了问题。 仔细看看后: 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();
希望没有更多的惊喜,即使我不喜欢在这里使用反射,我更愿意拥有实现类的方法实例,就像在EJB的InvocationContext中一样。
BTW,Spring没有AspectJ的原生方法:
public Object invoke(final MethodInvocation invocation) throws Throwable {}
返回接口而不是实现类。检查它是为了知识。
最诚挚的问候和感谢帮助,我真的很感激。 奥列格
答案 1 :(得分:2)
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();
这不是显而易见的,因为API应该受到责备。
另见Spring AOP: how to get the annotations of the adviced method。我自己没有测试过。 OP那里说它解决了他的问题。另一种用途是需要额外的@Around(annotation...)
注释。 (尝试将目标设置为仅METHOD
,并查看其行为方式)