从org.aspectj.lang.ProceedingJoinPoint获取模板/Generic java.lang.reflect.Method对象

从org.aspectj.lang.ProceedingJoinPoint获取模板/Generic java.lang.reflect.Method对象,java,spring,reflection,aop,aspectj,Java,Spring,Reflection,Aop,Aspectj,如果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; } 现在我

如果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 = 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
相反,我被迫自己使用反射获取方法对象,如下所示:

    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);
Person person = new Person("Oleg");
personService.save(person);
它会一直工作,直到您想要获得模板方法:

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

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) {}
最终类[]argumentTypes将包含一个元素,其类型为Person。所以这个电话:

pjp.getTarget().getClass().getMethod(methodName, argumentTypes);
将查找方法:

save(Person p)
而且不会找到它

所以问题是:如何获得java的template Method对象的实例,该对象表示被截取的确切方法?

我想其中一种方法就是用蛮力的方式: 获取参数超类/接口,并尝试使用这些类型获取方法,直到您不再获得NoSuchMethodException,但这显然很难看。有没有一个正常的清洁方式可以做到这一点

MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();
这一点并不明显,API对此负有责任


另见。我自己还没有测试过。那里的警员说这为他解决了这个问题。对于另一种用途,需要额外的
@Around(注释…
注释。(例如,尝试将目标设置为仅
方法
,并查看其行为)

好的,我的问题现在已解决。 在仔细查看以下内容后: methodSignature.getMethod() 返回我注意到它返回了接口,而不是实现类,当然接口上没有注释。这与EJB拦截器不同,在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的调用上下文中一样

顺便说一句,没有AspectJ的Spring原生方法:

public Object invoke(final MethodInvocation invocation) throws Throwable {}
返回接口,但也不实现类。为了知识也检查了一下

向你问好,谢谢你的帮助,我真的很感激。
Oleg

好的,首先,非常感谢您的回复。其次,我感到很遗憾的是,我没有很好地期待getSignature()的结果,没有注意到我可以从那里检索一个方法对象。它确实返回了它,谢谢你的提示。但实际上,我想要这个对象的原因是检索这个方法的注释,猜猜看是什么?它们被擦除了。截取的方法确实有anno。如果我使用pjp.getTarget().getClass().getMethod()的“错误”方式执行,则将返回annos。注释为@Retention(RetentionPolicy.RUNTIME)的所有注释。有什么建议吗?澄清一下:final MethodSignature MethodSignature=(MethodSignature)pjp.getSignature();final-Method=methodSignature.getMethod();最终注释[]annos=method.getAnnotations();如果以这种方式获取方法对象,“annos”变量将为null。我将您的答案标记为已接受,因为这是目标,但不幸的是,如果注释被删除,这样的对象将毫无帮助。我也试过:@Around(“execution(@foo.bar.annotation.MyAnnotation**.*(..))没有帮助。@Oleg-see@Bozho谢谢,我在搜索空问题时看到了这篇文章。从那篇文章中尝试了一切。我仍然会得到null或奇怪的异常(在绑定注释时)。我的anno被注释为RetentionPolicy.RUNTIME。不幸的是,这没有帮助。@Oleg-这很奇怪。尝试清洁你的环境。