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

时间:2011-03-06 17:24:54

标签: 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;
}

现在我需要截获的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,但这很简单。我可以做到这一点吗?

2 个答案:

答案 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,并查看其行为方式)