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

发布于 2024-10-20 07:23:33 字数 2354 浏览 5 评论 0原文

如果 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

美羊羊 2024-10-27 07:23:33

好吧,我的问题现在已经解决了。
仔细一看后发现:
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 的 InitationContext 中那样。

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

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

返回接口并且不实现类。为了长知识,也查了一下。

致以最诚挚的问候和感谢您的帮助,我真的很感激。
奥列格

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:

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

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:

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

寄居人 2024-10-27 07:23:33
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();

这并不是很明显,这要归咎于 API。

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

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

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 only METHOD for example and see how it behaves)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文