Spring AOP源码分析四

上篇文章我们分析了切点表达式与目标类匹配的前半部分,也就是类级别的匹配,而这篇文章我们就来看最最核心的一块,那就是方法级别的精确匹配。在开始分析之前,我们先来猜测一下方法精确匹配,可能会从哪几个维度进行匹配,首先我们知道方法中比较重要的“属性”分别有方法参数、方法名字、方法返回值等信息,那么方法精确匹配是不是从这几个维度下手,进行的匹配呢?上篇文章我们分析到了这个地方,大家先来回顾一下,如下图:
在这里插入图片描述
现在我们知道obtainPointcutExpression()方法返回的是一个PointcutExpressionImpl类的实例,那么接下来就会调用PointcutExpressionImpl类的matchesMethodExecution(methodToMatch)方法,来完成切点表达式和目标类的匹配。
那我们现在就来看下PointcutExpressionImpl类的matchesMethodExecution(methodToMatch)方法,此时我们会看到如下代码:

public ShadowMatch matchesMethodExecution(Method aMethod) {
		// 从这里进去匹配
		ShadowMatch match = matchesExecution(aMethod);
		if (MATCH_INFO && match.maybeMatches()) {
			System.out.println("MATCHINFO: method execution match on '" + aMethod + "' for '" + this.expression + "': "
					+ (match.alwaysMatches() ? "YES" : "MAYBE"));
		}
		return match;
	}


private ShadowMatch matchesExecution(Member aMember) {
		Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember, this.matchContext);
		// 这个是方法匹配的重点
		ShadowMatchImpl sm = getShadowMatch(s);
		sm.setSubject(aMember);
		sm.setWithinCode(null);
		sm.setWithinType(aMember.getDeclaringClass());
		return sm;
	}
	private ShadowMatchImpl getShadowMatch(Shadow forShadow) {
	// 切点表达式
		org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow);
		Test residueTest = Literal.TRUE;
		ExposedState state = getExposedState();
		if (match.maybeTrue()) {
			residueTest = pointcut.findResidue(forShadow, state);
		}
		ShadowMatchImpl sm = new ShadowMatchImpl(match, residueTest, state, parameters);
		sm.setMatchingContext(this.matchContext);
		return sm;
	}

我们可以看到在matchesMethodExecution()方法中又调用了matchesExecution(aMethod)方法来完成匹配,而matchesExecution(aMethod)方法中的重点是getShadowMatch(s)方法,在getShadowMatch(s)方法中,调用了pointcut.match(forShadow)完成了切点表达式的匹配,而pointcut.match(forShadow)的代码如下:

	public final FuzzyBoolean match(Shadow shadow) {
		if (shadow.shadowId == lastMatchedShadowId) {
			return lastMatchedShadowResult;
		}
		FuzzyBoolean ret;
		// this next test will prevent a lot of un-needed matching going on....
		if (shadow.getKind().isSet(couldMatchKinds())) {
			ret = matchInternal(shadow);
		} else {
			ret = FuzzyBoolean.NO;
		}
		lastMatchedShadowId = shadow.shadowId;
		lastMatchedShadowResult = ret;
		return ret;
	}

我们可以看到,这里又继续调用了matchInternal(shadow)方法完成了匹配,我们继续往下深入,此时matchInternal(shadow)代码如下:
在这里插入图片描述

protected FuzzyBoolean matchInternal(Shadow shadow) {
		if (shadow.getKind() != kind) {
			return FuzzyBoolean.NO;
		}

		if (shadow.getKind() == Shadow.SynchronizationLock && kind == Shadow.SynchronizationLock) {
			return FuzzyBoolean.YES;
		}
		if (shadow.getKind() == Shadow.SynchronizationUnlock && kind == Shadow.SynchronizationUnlock) {
			return FuzzyBoolean.YES;
		}
			
			// 进行签名匹配
		if (!signature.matches(shadow.getMatchingSignature(), shadow.getIWorld(), this.kind == Shadow.MethodCall)) {

			if (kind == Shadow.MethodCall) {
				warnOnConfusingSig(shadow);
				// warnOnBridgeMethod(shadow);
			}
			return FuzzyBoolean.NO;
		}

		return FuzzyBoolean.YES;
	}

我们可以看到,在matchInternal()方法刚进来时先做了一些判断,然后接着又调用了signature.matches(),从这个方法的名字来看,是对签名进行匹配,看起来离匹配的核心逻辑已经非常近了,接下来我们点进去看下这个signature.matches()的代码,大家看这里:
在这里插入图片描述

	public boolean matches(Member joinPointSignature, World world, boolean allowBridgeMethods) {
		// fail (or succeed!) fast tests...
		if (joinPointSignature == null) {
			return false;
		}
		if (kind != joinPointSignature.getKind()) {
			return false;
		}
		if (kind == Member.ADVICE) {
			return true;
		}

		// do the hard work then...
		boolean subjectMatch = true;
		boolean wantsAnnotationMatch = wantToMatchAnnotationPattern();
		JoinPointSignatureIterator candidateMatches = joinPointSignature.getJoinPointSignatures(world);
		while (candidateMatches.hasNext()) {
			JoinPointSignature aSig = candidateMatches.next();
			// System.out.println(aSig);
			// 精确匹配
			FuzzyBoolean matchResult = matchesExactly(aSig, world, allowBridgeMethods, subjectMatch);
			if (matchResult.alwaysTrue()) {
				return true;
			} else if (matchResult.alwaysFalse()) {
				return false;
			}
			// if we got a "MAYBE" it's worth looking at the other signatures
			// The first signature is the subject signature - and against it we must match modifiers/annotations/throws
			// see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html
			subjectMatch = false;
			// Early exit
			if (wantsAnnotationMatch) {
				return false;
			}
		}
		return false;
	}
	
	private FuzzyBoolean matchesExactly(JoinPointSignature aMember, World inAWorld, boolean allowBridgeMethods, boolean subjectMatch) {
		FuzzyBoolean matchesIgnoringAnnotations = FuzzyBoolean.YES;
		if (kind == Member.STATIC_INITIALIZATION) {
			// 静态初始化类型的匹配
			matchesIgnoringAnnotations = matchesExactlyStaticInitialization(aMember, inAWorld);
		} else if (kind == Member.FIELD) {
			// 属性精确匹配
			matchesIgnoringAnnotations = matchesExactlyField(aMember, inAWorld);
		} else if (kind == Member.METHOD) {
		  // 方法精确匹配,我们就是要匹配方法的,所以会走这里的逻辑
			matchesIgnoringAnnotations = matchesExactlyMethod(aMember, inAWorld, subjectMatch);
		} else if (kind == Member.CONSTRUCTOR) {
		   // 构造方法匹配
			matchesIgnoringAnnotations = matchesExactlyConstructor(aMember, inAWorld);
		}
		if (matchesIgnoringAnnotations.alwaysFalse()) {
			return FuzzyBoolean.NO;
		}

	}

在这个matches()方法中,最最核心的是调用了matchesExactly()方法,从方法名matchesExactly()来看,它的作用就是做精确匹配的。当我们看到matchesExactly()方法的代码后,我们发现,在这个方法中,它进一步区分了当前你要做的是属性的精确匹配,还是方法的精确匹配,又或者是构造方法的精确匹配。我们现在要做的当然是方法级别的精确匹配,所以就会进入if (kind == Member.METHOD)这个分支,接着就会调用matchesExactlyMethod()方法来完成方法级别的精确匹配。我们到这个方法中看下:
在这里插入图片描述

private FuzzyBoolean matchesExactlyMethod(JoinPointSignature aMethod, World world, boolean subjectMatch) {
 	     // 判断方法参数是否匹配,不匹配返回NO
		if (parametersCannotMatch(aMethod)) {
			// System.err.println("Parameter types pattern " + parameterTypes + " pcount: " + aMethod.getParameterTypes().length);
			return FuzzyBoolean.NO;
		}
		// OPTIMIZE only for exact match do the pattern match now? Otherwise defer it until other fast checks complete?
		// 判断方法名称是否匹配,不匹配返回NO
		if (!name.matches(aMethod.getName())) {
			return FuzzyBoolean.NO;
		}
		// Check the throws pattern
		// 判断方法抛出异常是否匹配,不匹配返回NO
		if (subjectMatch && !throwsPattern.matches(aMethod.getExceptions(), world)) {
			return FuzzyBoolean.NO;
		}

		// '*' trivially matches everything, no need to check further
		if (!declaringType.isStar()) {
			if (!declaringType.matchesStatically(aMethod.getDeclaringType().resolve(world))) {
				return FuzzyBoolean.MAYBE;
			}
		}

		// '*' would match any return value
		// 判断返回值类型是否匹配,不匹配返回NO
		// 如果切点表达式返回值类型为*,则表示可以匹配任何类型,就没必要进行下面的匹配,如果
		// 切点表达式返回值类型不是*,则对返回值类型进行匹配
		if (!returnType.isStar()) {
			boolean b = returnType.isBangVoid();
			if (b) {
				String s = aMethod.getReturnType().getSignature();
				if (s.length() == 1 && s.charAt(0) == 'V') {
					// it is void, so not a match
					return FuzzyBoolean.NO;
				}
			} else {
				if (returnType.isVoid()) {
					String s = aMethod.getReturnType().getSignature();
					if (s.length() != 1 || s.charAt(0) != 'V') {
						// it is not void, so not a match
						return FuzzyBoolean.NO;
					}
				} else {
					if (!returnType.matchesStatically(aMethod.getReturnType().resolve(world))) {
						// looking bad, but there might be parameterization to consider...
						if (!returnType.matchesStatically(aMethod.getGenericReturnType().resolve(world))) {
							// ok, it's bad.
							return FuzzyBoolean.MAYBE;
						}
					}
				}
			}
		}

		// The most simple case: pattern is (..) will match anything
		// 如果切点表达式方法参数只配置一个参数,且这个参数是.. ,那么表示可以匹配任何参数类型,此时返回YES
		if (parameterTypes.size() == 1 && parameterTypes.get(0).isEllipsis()) {
			return FuzzyBoolean.YES;
		}
		// 如果切点表达式配置的参数个数和目标方法的参数个数不相等时,那么直接返回NO
		if (!parameterTypes.canMatchSignatureWithNParameters(aMethod.getParameterTypes().length)) {
			return FuzzyBoolean.NO;
		}

		// OPTIMIZE both resolution of these types and their annotations should be deferred - just pass down a world and do it lower
		// down
		// ResolvedType[] resolvedParameters = world.resolve(aMethod.getParameterTypes());

		ResolvableTypeList rtl = new ResolvableTypeList(world, aMethod.getParameterTypes());
		// Only fetch the parameter annotations if the pointcut is going to be matching on them
		// 当方法参数上加了注解,就需要来匹配参数注解
		ResolvedType[][] parameterAnnotationTypes = null;
		if (isMatchingParameterAnnotations()) {
			parameterAnnotationTypes = aMethod.getParameterAnnotationTypes();
			if (parameterAnnotationTypes != null && parameterAnnotationTypes.length == 0) {
				parameterAnnotationTypes = null;
			}
		}
		// 对方法参数的类型进行匹配
		if (!parameterTypes.matches(rtl, TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) {
			// It could still be a match based on the generic sig parameter types of a parameterized type
			if (!parameterTypes.matches(new ResolvableTypeList(world, aMethod.getGenericParameterTypes()), TypePattern.STATIC,
					parameterAnnotationTypes).alwaysTrue()) {
				return FuzzyBoolean.MAYBE;
				// It could STILL be a match based on the erasure of the parameter types??
				// to be determined via test cases...
			}
		}

		// check that varargs specifications match
		// 对可变参数的处理,检查可变参数是否匹配
		if (!matchesVarArgs(aMethod, world)) {
			return FuzzyBoolean.MAYBE;
		}

		// passed all the guards..
		return FuzzyBoolean.YES;
	}

在这里插入图片描述
我们可以看到,首先调用了parametersCannotMatch(aMethod)方法来判断方法参数是否匹配,然后又调用了name.matches()判断方法名字是否匹配,接着又调用了throwsPattern.matches()判断方法抛出异常是否匹配。
而且我们再往下看,还发现了对方法返回值的匹配逻辑。说白了这里就是将增强中定义的切点表达式与目标方法进行匹配,也就是看下这个方法是否符合切点表达式的要求,而具体匹配的内容分别包含方法参数是否匹配、方法名字是否匹配、方法抛出异常是否匹配、方法返回值是否匹配等。我们知道,通过方法参数、方法名字、方法抛出异常、方法返回值这些信息都可以唯一确定一个方法了,所以aspectj就是通过分别判断方法参数、方法名字、方法抛出异常、方法返回值是够匹配,来确定目标类的方法是够匹配切点表达式的。
好,我们继续往下看,此时会看到下边这块代码:
在这里插入图片描述
结合变量的命名和注释来看,我们可以判断出这个parameterTypes其实就是切点表达式中的方法参数数组,那么parameterTypes.size() == 1满足的话说明此时切点表达式只配置了一个参数,并且此时parameterTypes.get(0).isEllipsis()满足的话,说明这个参数是一个省略号…

当整个条件parameterTypes.size() == 1 && parameterTypes.get(0).isEllipsis()的结果为true时,说明此时切点表达式的方法参数中,也就是那个括号中只配置了一个参数,并且这个参数配置的是省略号…,就比如我们日志增强配置的切点表达式,如下图:
在这里插入图片描述
这种情况就是切点表达式不限制目标方法的参数个数,因此不管你目标方法中有没有参数,或者有多少个参数,统统都满足,所以当parameterTypes.size() == 1 && parameterTypes.get(0).isEllipsis()这行代码满足时,直接返回了FuzzyBoolean.YES,此时代表匹配成功。那如果切点表达式方法参数个数和目标方法参数个数不相等呢?此时会返回什么?我们继续往下看,此时会看到这个判断:
在这里插入图片描述
这里调用了canMatchSignatureWithNParameters()方法进行判断,并且将目标方法的参数个数作为入参传了进去,再结合这个canMatchSignatureWithNParameters()方法的名字,我们大概判断出这个方法就是当切点表达式配置的参数个数和目标方法的参数个数不相等时,会直接返回NO,说白了就是不匹配。

这里代码比较多我们就不继续往下看了,我们就是要大概知道AspectJ是怎样通过我们的切点表达式去匹配我们的方法就行了,我们继续回到主干线上来:
在这里插入图片描述
当matches()方法返回后,那么canApply()方法就会返回true,如下图:

在这里插入图片描述
我们可以知道,只要目标类中任意一个方法与增强中的切点表达式匹配上,那么就直接返回true,此时就代表当前增强是适合这个目标类的,同时也表示这个目标类是需要设置为代理的。接着canApply(candidate, clazz, hasIntroductions)这行代码也返回了true,此时就会将这个匹配成功的增强Advisor放入到eligibleAdvisors集合中,如下图:
在这里插入图片描述这里匹配的时候是一个for循环,也就是目标类会和所有的增强Advisors都匹配一遍,最终将目标类匹配上的增强Advisor给放到这个eligibleAdvisors集合中作为结果返回。
接着findAdvisorsThatCanApply()方法将匹配到的增强Advisor返回,如下图:
在这里插入图片描述
然后在findEligibleAdvisors()方法中,也就是eligibleAdvisors进行排序,如下图:
在这里插入图片描述
因为刚开始讲AOP的时候,我们就讲了,切面类是可以实现Ordered接口,然后通过重写getOrder()方法来控制多个切面之间的执行顺序,所以这个sortAdvisors()方法,就会根据重写的getOrder()方法返回值,来对不同切面的增强进行排序,最后将排序后的增强作为最终结果进行返回。
接着getAdvicesAndAdvisorsForBean()方法接收到findEligibleAdvisors()方法返回的增强advisors后,将增强advisors转换成数组返回,如下图:
在这里插入图片描述
最后wrapIfNecessary()方法就接收到getAdvicesAndAdvisorsForBean()返回的增强集合specificInterceptors了,这个specificInterceptors就是目标类ProductServiceImpl匹配出来的增强,也就是拦截器,接着就会使用specificInterceptors来创建AOP动态代理了,代码如下:
在这里插入图片描述
到这里为止,获取目标类匹配的增强逻辑就分析完毕了,下一步就要利用获取到的这些增强来创建动态代理了!

我们梳理一下AOP代理的创建流程
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youngerone123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值