简体   繁体   English

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

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

This question would not have existed if AspectJ worked the same way as EJB interceptors work. 如果AspectJ与EJB拦截器的工作方式相同,则将不存在此问题。

Consider basic scenario the EJB-interceptor way: 考虑EJB拦截器方式的基本方案:

@AroundInvoke
public Object log(final InvocationContext ctx) throws Exception {
    // do stuff before
    final Object result = ctx.proceed();
    // do stuff after
    return result;
}

Now I need the Method object that's being intercepted. 现在,我需要被拦截的Method对象。 Here I can simply do this: 在这里,我可以简单地做到这一点:

        Method method = ctx.getMethod();

And that's it, after this I will be inspecting intercepted method's annotations. 就是这样,在此之后,我将检查拦截的方法的注释。

Now I'm working with application which is deployed to servlet container (Tomcat) and hence has no EJBs. 现在,我正在使用已部署到servlet容器(Tomcat)的应用程序,因此没有EJB。 AOP in it is implemented using Spring + AspectJ. 其中的AOP使用Spring + AspectJ实现。

The around method looks like this: 周围的方法如下所示:

@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;
}

Here I can no longer do this: 在这里,我不能再这样做:

Method method = pjp.getMethod(); // no such method exists

Instead I am forced to get the Method object myself using reflection as follows: 相反,我不得不自己使用反射来获取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);

It works until you want to get a hold of the template method: 它一直有效,直到您希望掌握模板方法为止:

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

Assume you invoke this method as follows: 假设您按以下方式调用此方法:

Person person = new Person("Oleg");
personService.save(person);

You will receive a: 您将收到:

Caused by: java.lang.NoSuchMethodException: foo.bar.domain.integration.database.dao.EntityService.save(foo.bar.domain.entity.Person)

which is thrown by: 抛出:

pjp.getTarget().getClass().getMethod()

The problem is that generics do not exist at runtime and the actual method signature is: 问题在于泛型在运行时不存在,实际的方法签名为:

public Identifiable save(Identifiable t) {}

final Class[] argumentTypes will contain one element and its type will be Person. final Class [] argumentsTypes将包含一个元素,其类型为Person。 So this call: 所以这个电话:

pjp.getTarget().getClass().getMethod(methodName, argumentTypes);

will be looking up method: 将查找方法:

save(Person p)

and will not find it. 并且不会找到它。

So the question is: how do I get instance of java's template Method object which represents the exact method that has been intercepted? 所以问题是:如何获取java的模板方法对象的实例,该对象表示已被拦截的确切方法?

I guess one of the ways is to do it the brute force way: get arguments superclasses/interfaces and try getting the method using those types until you no longer get the NoSuchMethodException but that is plain ugly. 我猜想其中一种方法是蛮力的:获取参数超类/接口,并尝试使用这些类型获取方法,直到不再获取NoSuchMethodException为止,但这很丑陋。 Is there a normal clean way I can do that? 有正常的清洁方法可以做到吗?

Okay I have now my problem solved. 好吧,现在我的问题解决了。 After a closer look at what: methodSignature.getMethod() returns I noticed it returned the interface instead of the implementing class, and of course there were no annotations on the interface. 仔细研究一下:methodSignature.getMethod()返回后,我注意到它返回了接口,而不是实现类,并且接口上当然没有注释。 This is different from EJB interceptors where getMethod() returns the implementing class method. 这与EJB拦截器不同,在EJB拦截器中,getMethod()返回实现的类方法。

So the final solution is this: 所以最终的解决方案是这样的:

    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());    
    }

and if you like, you can handle interface annotations here as well, if needed. 如果需要,还可以在此处处理接口注释。

Also notice this bit: method.getParameterTypes() without this it would still throw NoSuchMethodException so it's a good thing we could get the correct signature via ((MethodSignature)pjp.getSignature()).getMethod(); 还要注意这一点:method.getParameterTypes()如果没有此设置,它仍然会引发NoSuchMethodException,因此,我们可以通过((MethodSignature)pjp.getSignature())。getMethod();获得正确的签名是一件好事。

Hopefully, no more surprises, even though I am not happy about using reflection here, I would just prefer to have the method instance of the implementing class, as in InvocationContext of EJB. 希望没有更多的惊喜,即使我对在这里使用反射不满意,我也更喜欢拥有实现类的方法实例,就像在EJB的InvocationContext中一样。

BTW, Spring's native approach without AspectJ: BTW,Spring没有AspectJ的本机方法:

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

returns interface and not implementing class as well. 返回接口并且不实现类。 Checked it for the sake of knowledge too. 也为了知识而对其进行了检查。

Best Regards and thanks for helping, I really appreciate it. 最好的问候,并感谢您的帮助,我真的很感激。 Oleg 奥列格

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

This is not immediately obvious, for which the API is to blame. 这不是应该立即归咎于API。

Also see Spring AOP: how to get the annotations of the adviced method . 另请参见Spring AOP:如何获取所建议方法的注释 I haven't tested this myself. 我自己还没有测试过。 The OP there says it solved the issue for him. 那里的OP说它为他解决了这个问题。 For another use an additional @Around(annotation...) annotation was needed. 对于其他用途,需要额外的@Around(annotation...)注释。 (Try setting the target to be only METHOD for example and see how it behaves) (例如,尝试将目标设置为仅METHOD ,并查看其行为)

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

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