@Aspect结合@Order能建立具有最高优先级的切面?

文章目录

      • 疑问
      • 探究原因
      • 总结

疑问

一般情况下,比如说@around接口就只有一个参数ProceedingJoinPoint,是不会有问题的(只是侥幸),但是使用多个参数时,就会抛出异常。

如下,around方法有两个参数时,程序抛异常:

@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
public class TestOrderAspect {

    @Pointcut("@annotation(testOrder)")
    public void pointCut(TestOrder testOrder) {
    }

    @Around("pointCut(testOrder)")
    public Object around(ProceedingJoinPoint point, TestOrder testOrder) throws Throwable {
        Object result;
        result = point.proceed();
        return result;
    }
}

探究原因

CglibAopProxy的intercept方法中,会先获取一个拦截链,如果该链不为空,则需要创建methodInvocation

@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Object target = null;
   TargetSource targetSource = this.advised.getTargetSource();
   try {
      ......
      // 获取拦截链    
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         //不创建methodinvocation,直接调用目标方法
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // 1.需要创建methodInvocation
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      ......
   }
}
 
  

创建完MethodInvocation后,会调用ReflectiveMethodInvocation.proceed(),进而会调用AspectJExpressionPointcut的matches方法,进行切点匹配

@Override
	public boolean matches(Method method, Class targetClass, Object... args) {
	    ......
		ProxyMethodInvocation pmi = null;
		Object targetObject = null;
		Object thisObject = null;
		try {
            // 尝试获取MethodInvocation,获取不到会抛异常
            // ExposeInvocationInterceptor才会暴露出来MethodInvocation,所以要在链首位
			MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
			targetObject = mi.getThis();
			if (!(mi instanceof ProxyMethodInvocation)) {
				throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
			}
			pmi = (ProxyMethodInvocation) mi;
			thisObject = pmi.getProxy();
		}
		catch (IllegalStateException ex) {
			......
		}
        try {
			......
			if (pmi != null && thisObject != null) {  // there is a current invocation
				RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(getShadowMatch(method, method));
				if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
					return false;
				}
				if (joinPointMatch.matches()) {
                      // 绑定参数信息,即绑定除ProceedingJoinPoint外的参数,非常重要!!!
					bindParameters(pmi, joinPointMatch);
				}
			}
            ......
	}

返回对应的MethodInvocation:但是如果没有将ExposeInvocationInterceptor加至拦截链首部,则无法暴露对应 的MethodInvocation,故而抛异常。

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!");
   }
   return mi;
}

虽然会被matchesf方法捕获掉异常,但是由于pmi和thisObject为null,导致最终没有执行bindParameters绑定参数信息,所以,只要是2个及以上参数,则会报错。

总结

在使用@Order来控制切面优先级时,最好不要直接定义该切面优先级为Ordered.HIGHEST_PRECEDENCE,导致无法暴露methodInvocation,一旦切面中定义的方法参数超过正常情况,就会报错。

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