使用 Spring AOP 时,在单个连接点上具有参数绑定的多个 Around 建议会导致错误

Multiple Around advices with argument binding on a single join point causes error when using Spring AOP

我在一个方法上写了 2 个注释和 2 个 Around 建议来处理每个注释值。

连接点方法是这样的:

@CacheFetch(cacheName = CacheManager.CACHE_DATASOURCE_INFO)
@TenantAware(method = OperationMethod.OPERATION, operation = OperationType.GET)
public DataSourceInfo fetchDataSource(String sourceId) {...}

这样的建议 1:

@Around("within(com.xx.yy.zz..*) && @annotation(fetch)")
public Object fetchFromCache(ProceedingJoinPoint pjp, CacheFetch fetch) throws Throwable {...}

像这样的建议2:

@Around("isXXX() && @annotation(tenantAware)")
public Object handleTenantAware(ProceedingJoinPoint pjp, TenantAware tenantAware) throws Throwable {...}

这两个建议的区别是Aspect 类 和Aspect 类 都实现了Ordered 接口。程序执行到fetchDataSource连接点方法时,出现异常:

java.lang.IllegalStateException: Required to bind 2 arguments, but only bound 1 (JoinPointMatch was NOT bound in invocation)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.argBinding(AbstractAspectJAdvice.java:591)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:616)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)

如果我删除其中一个建议,错误就会消失,而其余的建议会正常工作。我搜索了这个问题,大部分结果来自非常旧的 spring 版本。

我当前的 Spring 框架和方面版本是 4.1.6。我尝试升级到4.1.9和4.3.20,问题依旧。

恕我直言,上面的代码应该可以工作,而且我自己也没有发现任何错误。我不确定这是一个错误还是其他我不知道的东西。任何帮助将不胜感激。谢谢。

问题已解决,但仍有疑问。

我正在使用 2 方面 类,顺序设置为 Ordered.HIGHEST_PRECEDENCEOrdered.LOWEST_PRECEDENCE。如果我将订单值 Ordered.HIGHEST_PRECEDENCE 替换为另一个值,错误就会消失。很奇怪的现象,异常显示与实际原因无关。有谁知道真正的原因吗?

这是由于ExposeInvocationInterceptor not load firstly, and it will cause userAttributes不会通过Spring Bean(for CacheFetch)注入参数造成的。

您还可以在 :

中找到 comment
No MethodInvocation found: Check that an AOP invocation is in progress, 
and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, 
note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!

还有一个 issue 尝试使用 PriorityOrdered 来解决这个问题,但它似乎仍然没有进行。

所以对于你的问题,有两种方案可以解决:

  1. Ordered改成HIGHEST_PRECEDENCE,也许HIGHEST_PRECEDENCE+1

  2. 通过ApplicationContext.getBean

  3. 手动注入bean

我发现另一种情况可能会使 Exposeinvocationinterceptor 中的调用无效,导致 org.springframework.aop.aspectj.aspectjexpressionpointcut ා matches (java.lang.reflect.method, java.lang.class <? >, Java. Lang.Object...) 无法获得有效的方法调用。

具体情况:当有多个切面时,当一个高优先级的切面异步处理时joinpoint.Processed(),下一个切面会切换到新的线程,所以无法获取到ThreadLocal变量。