繁体   English   中英

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

[英]Getting a Template/Generic java.lang.reflect.Method object from org.aspectj.lang.ProceedingJoinPoint

如果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("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的模板方法对象的实例,该对象表示已被拦截的确切方法?

我猜想其中一种方法是蛮力的:获取参数超类/接口,并尝试使用这些类型获取方法,直到不再获取NoSuchMethodException为止,但这很丑陋。 有正常的清洁方法可以做到吗?

好吧,现在我的问题解决了。 仔细研究一下: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的InvocationContext中一样。

BTW,Spring没有AspectJ的本机方法:

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

返回接口并且不实现类。 也为了知识而对其进行了检查。

最好的问候,并感谢您的帮助,我真的很感激。 奥列格

MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();

这不是应该立即归咎于API。

另请参见Spring AOP:如何获取所建议方法的注释 我自己还没有测试过。 那里的OP说它为他解决了这个问题。 对于其他用途,需要额外的@Around(annotation...)注释。 (例如,尝试将目标设置为仅METHOD ,并查看其行为)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM