【十二】Spring源码分析之AOP----匹配出作用于被代理类Advisor

一、简介:

AOP源码分析主要分为5个篇章:前面2篇已经讲了

1. 注册、实例化、初始化AnnotationAwareAspectJAutoProxyCreator

【十】Spring源码分析之AOP----注册、实例化、初始化AnnotationAwareAspectJAutoProxyCreator
2. 扫描容器中的切面,创建PointcutAdvisor对象
【十一】Spring源码分析之AOP----AnnotationAwareAspectJAutoProxyCreator扫描@Aspect,创建Advisor

3. 目标bean和每个Advisor匹配,找出该bean在被代理的时候需要用到哪些Advisor。就是本篇。

4. 创建代理对象

【十三】Spring源码分析之AOP----生成代理对象

5. 被代理类的方法一次调用流程。

【十四】Spring源码分析之AOP----JdkDynamicAopProxy代理对象invoke调用

这一篇讲匹配出作用于被代理类Advisor

入口:AbstractAdvisorAutoProxyCreator的getAdvicesAndAdvisorsForBean方法

方法调用栈:

getAdvicesAndAdvisorsForBean方法

源码:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

该方法调用findeEligibleAdvisors方法获得该目标bean对应的Advisor然后返回

二、 findeEligibleAdvisors

源码:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {

        // 找到所有的advisor
		List<Advisor> candidateAdvisors = findCandidateAdvisors();

        // 从所有的adivisor中选出可用的
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

        // 提供的hook方法,用于对目标Advisor进行扩展
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {

            // 对该bean用到的Advisor排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

 做了4件事:

1.findCandidateAdvisors方法,得到所有的Advisor,该方法在上一章中介绍过。虽然在之前的shouldSkip()方法中已为当前bean生成Advisor放在缓存中,在这里获取时可以直接从缓存中获取Advisor。

2.findAdvisorsThatCanApply方法从所有的Advisor中找到可用于当前bean的Advisor

3. 提供的hook方法,用于对目标Advisor进行扩展.

4.对该Bean用到的Advisor排序

三、 findAdvisorsThatCanApply

找出能够应用于该bean的Advisor

源码:

protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

        // 标记为当前代理的BeanName
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {

            // 调用AopUitls.findAdvisorsThatCanApply方法得到可用的的Advisor
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {

            // 标记为当前代理的BeanName=null
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

 做了3件事:

1.标记为当前代理的BeanName

2.调用AopUitls.findAdvisorsThatCanApply方法得到可应用于该bean的Advisor

3.标记为当前代理的BeanName=null

 四、 AopUtils类的findAdvisorsThatCanApply方法

源码:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {

        // 如果候选Advisor为空直接返回
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();

        // 遍历候选Advisor
		for (Advisor candidate : candidateAdvisors) {

            // 如果Advisor是IntroductionAdvisor 的实例并且是可用
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                // 加入到返回列表
				eligibleAdvisors.add(candidate);
			}
		}

       
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();

        // 遍历候选Advisor
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
            // 如果不是IntroductionAdvisor的实例,但是可用
			if (canApply(candidate, clazz, hasIntroductions)) {

                // 加入到返回列表
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

 做了3件事:

1.如果候选Advisor为空,直接返回

2.遍历候选Advisor,如果Advisor是IntroductionAdvisor的实例并且是该目标Bean要用的(canAppliy返回为true,这个方法后面介绍),加入到返回列表中。

3.遍历候选Advisor,如果不是IntroductionAdvisor的实例,但是该目标Bean要用的(canAppliy返回为true,这个方法后面介绍),加入到返回列表中。

找出能够应用于该bean的Advisor

五、 AopUtils的canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions)方法

找出能够应用于该bean的Advisor

源码: 

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pca = (PointcutAdvisor) advisor;
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

做了3件事:

1.如果Advisor是IntroductionAdvisor的实例,判断advisor的ClassFilter是否和被代理的类匹配,并且返回判断结果。

2.如果Advisor是PointcutAdvisor的实例,调用另一个canApply方法 。

3.既不是IntroductionAdvisor的实例也不是PointcutAdvisor的实例,直接返回true。

六、  canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)方法

源码:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");

        // 如果pointcut的classfilter和被代理的类不匹配,返回false
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

        // 如果这个Advisor对任何方法都匹配,则直接返回true
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

        // 如果methodMatcher是IntroductionAwareMethodMatcher的实例,把methodMatcher赋值给IntroductionAwareMethodMatcher。
		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

        // 得到这个bean的所有接口
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

        // 把该bean也加入到它自己的接口集合中。
		classes.add(targetClass);

        // 遍历接口集合
		for (Class<?> clazz : classes) {

            // 得到实现的接口类的所有方法(包括父类方法),
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);

            // 遍历这些方法
			for (Method method : methods) {

                // 如果 IntroductionAwareMethodMatcher或者methodMatcher和目标bean的方法匹配,返回true。
				if ((introductionAwareMethodMatcher != null &&
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

做了5件事:

1.如果pointcut的ClassFilter(这里ClassFilter指的@Aspect注解中使用的切点表达式)和被代理的类不匹配,返回false。

2.得到该pointcut切点中配置的所有方法,即是到底哪些方法是这个pointcut切点。如果这个Advisor对任何方法都匹配,则直接返回true。

3.如果methodMatcher是IntroductionAwareMethodMatcher的实例,把methodMatcher赋值给IntroductionAwareMethodMatcher。

4.得到这个目标bean的所有接口集合,把该目标bean也加入到它自己的接口集合中。遍历接口集合,得到自己或实现的接口类的所有方法(包括父类方法)。

5.遍历这些方法,如果 IntroductionAwareMethodMatcher或者methodMatcher(MethodMatcher主要指的是@Before,@After等注解中使用的切点表达式)和目标bean的方法匹配,返回true。

七、 match

判断被代理的目标bean的方法是否使用这个Advisor。

比如说被代理类叫testController,该类有个方法叫test。切面@Aspect类有2个advice增强方法,before方法叫testBefore,after方法叫testAfter。那么这个被代理类testController的test方法到底是在调用test前会调用testBefore?还是在test方法被调用后调用testAfter方法?还是test在被调用前和被调用后都需要增强?就是在这个match方法中匹配的。

AspectJExpressionPointcut类的matches方法 

7.1 matches(Class<?> targetClass)

源码:

public boolean matches(Class<?> targetClass) {
		checkReadyToMatch();
		try {
			try {
				return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
			}
			catch (ReflectionWorldException ex) {
				logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
				// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
				PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
				if (fallbackExpression != null) {
					return fallbackExpression.couldMatchJoinPointsInType(targetClass);
				}
			}
		}
		catch (Throwable ex) {
			logger.debug("PointcutExpression matching rejected target class", ex);
		}
		return false;
	}

做了1件事:

 对类进行过滤匹配这里ClassFilter指的@Aspect注解中使用的切点表达式

7.2 matches(Method method, Class<?> targetClass, boolean beanHasIntroductions)

对方法进行过滤匹配,MethodMatcher主要指的是@Before,@After等注解中使用的切点表达式

源码:

public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
		checkReadyToMatch();

        // 得到特殊方法,比如被重写的方法、接口类的实现方法、桥接方法
		Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);

        // 将对切点表达式解析后的结果与匹配的目标方法封装为一个ShadowMatch对象,并且对目标方法进行匹配,匹配的结果将存储在ShadowMatch.match参数中
		ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);

		// Special handling for this, target, @this, @target, @annotation
		// in Spring - we can optimize since we know we have exactly this class,
		// and there will never be matching subclass at runtime.

		if (shadowMatch.alwaysMatches()) {
			return true;
		}
		else if (shadowMatch.neverMatches()) {
			return false;
		}
		else {
			// the maybe case

            // 不确认能否匹配的时候,判断是否有Introduction类型的Advisor
			if (beanHasIntroductions) {
				return true;
			}
			// A match test returned maybe - if there are any subtype sensitive variables
			// involved in the test (this, target, at_this, at_target, at_annotation) then
			// we say this is not a match as in Spring there will never be a different
			// runtime subtype.

            // 如果不确认能否匹配,则将匹配结果封装为一个RuntimeTestWalker,在方法运行时可进行动态匹配
			RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
			return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
		}
	}

做了5件事:

1. 得到特殊方法,比如被子类重写的方法、接口类的实现方法、桥接方法

2. 将对切点表达式解析后的结果与匹配的目标方法封装为一个ShadowMatch对象,并且对目标方法进行匹配,匹配的结果将存储在ShadowMatch.match参数中

3. 不确认能否匹配的时候,判断是否有Introduction类型的Advisor

4. 不确认能否匹配的时候,判断是否有Introduction类型的Advisor

5.如果不确认能否匹配,则将匹配结果封装为一个RuntimeTestWalker,在方法运行时可进行动态匹配

7.2.1 getMostSpecificMethod

源码:

	public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {

        // 解析Method  得到子类覆盖的方法
		Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
		// If we are dealing with method with generic parameters, find the original method.

        // 这是一个桥接方法
		return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
	}

做了2件事:

1. ClassUtils.getMostSpecificMethod()得到子类覆盖的方法

2.BridgeMethodResolver.findBridgedMethod()。返回桥接方法

java编译器采用bridge方法来兼容本该使用泛型的地方使用了非泛型的用法的问题。

7.2.2  getShadowMatch方法

真正做对切点节点匹配的逻辑的地方

private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
		// Avoid lock contention for known Methods through concurrent access...

        // 从缓存中获取ShadowMatch数据,如果缓存中存在则直接返回
		ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
		if (shadowMatch == null) {
			synchronized (this.shadowMatchCache) {
				// Not found - now check again with full lock...
				PointcutExpression fallbackExpression = null;
				Method methodToMatch = targetMethod;
				shadowMatch = this.shadowMatchCache.get(targetMethod);
				if (shadowMatch == null) {
					try {
						try {

                // 解析切点表达式与当前方法进行匹配,将匹配后的结果封装成ShadowMatch返回
							shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
						}
						catch (ReflectionWorldException ex) {
							// Failed to introspect target method, probably because it has been loaded
							// in a special ClassLoader. Let's try the declaring ClassLoader instead...

                // 如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析
							try {
								fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
								if (fallbackExpression != null) {

                        // 使用回调切点表达与目标方法进行匹配
									shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
								}
							}
							catch (ReflectionWorldException ex2) {
								fallbackExpression = null;
							}
						}
						if (shadowMatch == null && targetMethod != originalMethod) {

                        // 如果目标方法与当前切点表达式匹配失败,则用其原始方法与切点表达式匹配
							methodToMatch = originalMethod;
							try {
								shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
							}
							catch (ReflectionWorldException ex3) {
								// Could neither introspect the target class nor the proxy class ->
								// let's try the original method's declaring class before we give up...
								try {
									fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
									if (fallbackExpression != null) {
										shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
									}
								}
								catch (ReflectionWorldException ex4) {
									fallbackExpression = null;
								}
							}
						}
					}
					catch (Throwable ex) {
						// Possibly AspectJ 1.8.10 encountering an invalid signature
						logger.debug("PointcutExpression matching rejected target method", ex);
						fallbackExpression = null;
					}
					if (shadowMatch == null) {

                    // 如果目标方法和原始方法都与切点表达式匹配失败,就封装一个不匹配的结果
						shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
					}

                    // 如果通过匹配结果无法判断,就将匹配得到的ShadowMatch和回调的ShadowMatch封装到DefensiveShadowMatch中
					else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
						shadowMatch = new DefensiveShadowMatch(shadowMatch,
								fallbackExpression.matchesMethodExecution(methodToMatch));
					}

                    // 将匹配结果缓存起来
					this.shadowMatchCache.put(targetMethod, shadowMatch);
				}
			}
		}
		return shadowMatch;
	}

做了6件事:

1.从缓存中获取ShadowMatch数据,如果缓存中存在则直接返回

2.如果缓存中不存在,解析切点表达式与当前方法进行匹配,将匹配后的结果封装成ShadowMatch返回。如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析,使用回调切点表达与目标方法进行匹配。

3.如果目标方法与当前切点表达式匹配失败,则用其原始方法与切点表达式匹配。如果匹配失败,则在目标方法上找切点表达式,组装成为一个回调切点表达式,并且对回调切点表达式进行解析,使用回调切点表达与目标方法进行匹配。

4. 如果目标方法和原始方法都与切点表达式匹配失败,就封装一个不匹配的结果。

5.如果通过匹配结果无法判断,就将匹配得到的ShadowMatch和回调的ShadowMatch封装到DefensiveShadowMatch中。

6.将匹配结果缓存起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值