今天在调整日志纪录切面执行顺序时出现了上述错误,导致代码如下:
...
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LogRecordAspect
将切面设置为最优先执行报错,解决方案将优先级调低
...
@Order(Ordered.HIGHEST_PRECEDENCE+1)
public class LogRecordAspect
越小越优先。
方案已经给出来了,但是这个错误报出是百思不得其解,错误说的是需要2个参数但是只绑定了1个,跟我优先级有什么关系?
带着满脑袋的黑人问号,开始了源码探究之旅。
结果,错误并不是如题所述的,而是另一个异常。
该方法在org.springframework.aop.interceptor.ExposeInvocationInterceptor
类中
public static MethodInvocation currentInvocation() throws IllegalStateException {
MethodInvocation mi = invocation.get();
if (mi == null) {
throw new IllegalStateException(
"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! " +
"In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " +
"must be invoked from the same thread.");
}
return mi;
}
意思是未获取到ExposeInvocationInterceptor
拦截器,该拦截器的优先级是PriorityOrdered.HIGHEST_PRECEDENCE + 1
,其他拦截器必须在它之后,它必须是第一个执行的
@Override
public int getOrder() {
return PriorityOrdered.HIGHEST_PRECEDENCE + 1;
}
那为什么会报错题目中的异常呢?那是因为外层将其捕获并转换成了debug
级别的日志。
同时bindParameters
方法没有执行到,导致后续获取时候为空。
bindParameters
会设置org.springframework.aop.framework.ReflectiveMethodInvocation
中的Map
属性
public boolean matches(Method method, Class<?> targetClass, Object... args) {
obtainPointcutExpression();
...
try {
//这个地方抛出异常
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
...
pmi = (ProxyMethodInvocation) mi;
thisObject = pmi.getProxy();
}
catch (IllegalStateException ex) {
// No current invocation...
//纪录debug日志
if (logger.isDebugEnabled()) {
logger.debug("Could not access current invocation - matching with limited context: " + ex);
}
}
try {
JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
if (pmi != null && thisObject != null) { // there is a current invocation
...
//这个地方bindParameters如法执行到,所以参数就少了一个
if (joinPointMatch.matches()) {
bindParameters(pmi, joinPointMatch);
}
}
return joinPointMatch.matches();
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to evaluate join point for arguments " + Arrays.asList(args) +
" - falling back to non-match", ex);
}
return false;
}
}
matches
方法在org.springframework.aop.framework.ReflectiveMethodInvocation
的proceed()
方法中
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//逐一获取拦截器
Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
//执行match
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
//执行拦截器
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
后续执行dm.interceptor.invoke(this)
方法,然后进入到org.springframework.aop.aspectj.AspectJAroundAdvice.invoke
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
//这里去获取连接的时候发现没有
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
getJoinPointMatch
其实就是去获取上面 bindParameters方法,设置的Map
属性,所以会导致JoinPointMatch jpm = null
在org.springframework.aop.aspectj.AbstractAspectJAdvice
的argBinding
回去检测需要的参数和绑定的参数数量是否一直,这时候就发现少了一个JoinPoint
就报出了如题的异常。
protected Object[] argBinding(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
@Nullable Object returnValue, @Nullable Throwable ex) {
...
//jpMatch为空,可以进入源码看一下,有一个if条件未进入导致数量不匹配
//由于代码太长就不贴上来了
if (numBound != this.parameterTypes.length) {
throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
" arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
}
return adviceInvocationArgs;
}
对源码感兴趣的同学可以关注我(^U^)ノ~YO,将不定期跟更新自己的体会和一些源码理解分享。