Required to bind 2 arguments, but only bound 1 (JoinPointMatch was NOT bound in invocation)异常

文章目录

  • 问题
  • 原因

问题

今天在调整日志纪录切面执行顺序时出现了上述错误,导致代码如下:

...
@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 userAttributes属性

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.ReflectiveMethodInvocationproceed()方法中

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 userAttributes属性,所以会导致JoinPointMatch jpm = null

org.springframework.aop.aspectj.AbstractAspectJAdviceargBinding回去检测需要的参数和绑定的参数数量是否一直,这时候就发现少了一个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,将不定期跟更新自己的体会和一些源码理解分享。

你可能感兴趣的:(java,java,spring)