从 org.aspectj.lang.ProceedingJoinPoint 获取模板/通用 java.lang.reflect.Method 对象
如果 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 的 template Method 对象的实例,该对象代表已被拦截的确切方法?
我想其中一种方法是暴力破解方式: 获取参数超类/接口,并尝试使用这些类型获取方法,直到不再得到 NoSuchMethodException,但这实在是丑陋。有没有一种正常的干净方法可以做到这一点?
This question would not have existed if AspectJ worked the same way as EJB interceptors work.
Consider basic scenario the EJB-interceptor way:
@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. 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. AOP in it is implemented using 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:
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. 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?
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. Is there a normal clean way I can do that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好吧,我的问题现在已经解决了。
仔细一看后发现:
methodSignature.getMethod()
返回我注意到它返回了接口而不是实现类,当然接口上没有注释。这与 EJB 拦截器不同,EJB 拦截器的 getMethod() 返回实现类方法。
所以最终的解决方案是这样的:
如果您愿意,您也可以在需要时在这里处理接口注释。
另请注意这一点:method.getParameterTypes()
如果没有这个,它仍然会抛出NoSuchMethodException,所以我们可以通过 ((MethodSignature)pjp.getSignature()).getMethod(); 获得正确的签名,这是一件好事。
希望不会再有什么意外,尽管我不喜欢在这里使用反射,但我只是更喜欢拥有实现类的方法实例,就像 EJB 的 InitationContext 中那样。
顺便说一句,Spring 的原生方法没有 AspectJ:
返回接口并且不实现类。为了长知识,也查了一下。
致以最诚挚的问候和感谢您的帮助,我真的很感激。
奥列格
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. This is different from EJB interceptors where getMethod() returns the implementing class method.
So the final solution is this:
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();
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.
BTW, Spring's native approach without AspectJ:
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
这并不是很明显,这要归咎于 API。
另请参阅Spring AOP:如何获取建议方法的注释。我自己没有测试过这个。那里的OP说这为他解决了这个问题。对于其他用途,需要额外的
@Around(annotation...)
注释。 (例如,尝试将目标仅设置为METHOD
并查看其行为方式)This is not immediately obvious, for which the API is to blame.
Also see Spring AOP: how to get the annotations of the adviced method. I haven't tested this myself. The OP there says it solved the issue for him. For another use an additional
@Around(annotation...)
annotation was needed. (Try setting the target to be onlyMETHOD
for example and see how it behaves)