基于 Spring Framework v5.2.6.RELEASE
概述
前面几篇文章分析了用于创建 AOP 代理的后处理器,如何从容器中找到所有的增强逻辑对应的 Advisor 对象。查找的过程由后处理器的findCandidateAdvisors
方法执行,得到结果后,返回到findEligibleAdvisors
方法中。
接下来,后处理器将会从这些 Advisor 中,筛选出与当前处理的 Bean 实例匹配的 Advisor。本文将继续分析后面的代码。
查找匹配的 Advisor
先查看一下findEligibleAdvisors
方法的代码。
// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisorsprotectedList<Advisor>findEligibleAdvisors(Class<?>beanClass, StringbeanName) { List<Advisor>candidateAdvisors=findCandidateAdvisors(); List<Advisor>eligibleAdvisors=findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors=sortAdvisors(eligibleAdvisors); } returneligibleAdvisors; }
之前的几篇文章,分析的都是方法中的第一行代码,也就是findCandidateAdvisors
方法的原理,在这个方法执行完,获得到 Advisor 列表candidateAdvisors
之后,会在下一步通过findAdvisorsThatCanApply
方法,从candidateAdvisors
中找出与当前处理的 Bean 实例匹配的 Advisor 集合。
我们进入findAdvisorsThatCanApply
方法。
// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApplyprotectedList<Advisor>findAdvisorsThatCanApply( List<Advisor>candidateAdvisors, Class<?>beanClass, StringbeanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { returnAopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } }
其中核心的逻辑只有一行,就是调用 AopUtils 的findAdvisorsThatCanApply
方法,我们进入这个方法。
这个方法中的逻辑,我将分成几部分来分析。
if (candidateAdvisors.isEmpty()) { returncandidateAdvisors; } List<Advisor>eligibleAdvisors=newArrayList<>();
首先判断参数传入的 Advisor 集合是不是空的,如果是空的,那就不需要筛选了,直接返回空集合即可。如果不是空的,那么初始化一个 Advisor 集合eligibleAdvisors
,接下来的逻辑就是向这个集合中添加内容,最终这个集合会作为方法的结果返回。
接下来进入第一个for
循环语句。
for (Advisorcandidate : candidateAdvisors) { if (candidateinstanceofIntroductionAdvisor&&canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } }
此处会遍历candidateAdvisors
中的所有 Advisor 进行遍历,并将符合if
语句判断条件的添加到eligibleAdvisors
中。这里需要当前遍历到的 Advisor 同时满足两个判断条件:
- 它实现了 IntroductionAdvisor 接口;
canApply
方法返回true
。
这里先了解一下 IntroductionAdvisor,先看 Advisor 相关类型的关系。
如果你看过我之前的文章,这里可以看到一些熟悉的类型。
对于通过 XML 配置的切面信息,每一个增强方法会被创建成一个 AspectJPointcutAdvisor 类型的 Advisor 对象,而对于注解配置的切面信息,每一个增强方法会被创建成一个 InstantiationModelAwarePointcutAdvisorImpl 类型的 Advisor 对象。以上二者都实现了 PointcutAdvisor 接口。
而 IntroductionAdvisor 接口与 PointcutAdvisor 接口都是 Advisor 的子接口,它的实现类之一 DeclareParentsAdvisor 我们之间在分析代码时也见过,如果一个切面配置类的某个字段被标记了@DeclareParents
注解,那么它就会有一个对应的 DeclareParentsAdvisor 类型的 Advisor 对象。
由此可知,平时我们配置的增强方法对应的都是 PointcutAdvisor 的实现,那 IntroductionAdvisor 是什么呢?PointcutAdvisor 的增强目标可以精确到一个方法,而 IntroductionAdvisor 增强是针对类的,因此我们很少用到。
然后我们再看一下canApply
方法。
// org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>)publicstaticbooleancanApply(Advisoradvisor, Class<?>targetClass) { returncanApply(advisor, targetClass, false); } // org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)publicstaticbooleancanApply(Advisoradvisor, Class<?>targetClass, booleanhasIntroductions) { if (advisorinstanceofIntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } elseif (advisorinstanceofPointcutAdvisor) { PointcutAdvisorpca= (PointcutAdvisor) advisor; returncanApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies.returntrue; } }
只看 IntroductionAdvisor 部分的逻辑,其实就是判断当前的 IntroductionAdvisor 是不是匹配目标类型。
回到findAdvisorsThatCanApply
方法中,会先将candidateAdvisors
中匹配当前类型的 IntroductionAdvisor
先添加到eligibleAdvisors
中。
再接着看后面的代码。
booleanhasIntroductions=!eligibleAdvisors.isEmpty(); for (Advisorcandidate : candidateAdvisors) { if (candidateinstanceofIntroductionAdvisor) { // already processedcontinue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } }
首先,声明一个hasIntroductions
变量,表示上一步中是否找到了匹配的 IntroductionAdvisor。然后,再次遍历candidateAdvisors
,跳过 IntroductionAdvisor,也就是说,这个for
循环之处理 PointcutAdvisor,通过canApply
方法的判断,再将符合条件的 PointcutAdvisor 添加到eligibleAdvisors
中。
这里我们再一次进入canApply
方法查看 PointcutAdvisor 的部分。
// org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)publicstaticbooleancanApply(Advisoradvisor, Class<?>targetClass, booleanhasIntroductions) { if (advisorinstanceofIntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } elseif (advisorinstanceofPointcutAdvisor) { PointcutAdvisorpca= (PointcutAdvisor) advisor; returncanApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies.returntrue; } }
判断的逻辑交给了另外一个canApply
方法,进入这个重载方法。
我们分步解析。
if (!pc.getClassFilter().matches(targetClass)) { returnfalse; }
首先,先看类型是否能够匹配,如果类型都不能匹配,那就没必要再匹配方法了。
MethodMatchermethodMatcher=pc.getMethodMatcher(); if (methodMatcher==MethodMatcher.TRUE) { // No need to iterate the methods if we're matching any method anyway...returntrue; }
然后再看切入点是否是匹配类中的所有方法,如果是的话,则直接返回true
。
IntroductionAwareMethodMatcherintroductionAwareMethodMatcher=null; if (methodMatcherinstanceofIntroductionAwareMethodMatcher) { introductionAwareMethodMatcher= (IntroductionAwareMethodMatcher) methodMatcher; }
接下来,会判断methodMatcher
是否是 IntroductionAwareMethodMatcher 接口的实现,如果是,则强转类型,赋值给introductionAwareMethodMatcher
变量。这里我们来分析一下methodMatcher
的类型。
methodMatcher
的来源是上述代码中的pc.getMethodMatcher()
,也就是从切入点对象中获取到的,如果你看过之前的源码分析文章,可以知道,无论是通过 XML 配置还是注解配置,切入点都会被解析成一个 AspectJExpressionPointcut 类型的 Bean,我们找到 AspectJExpressionPointcut 中的getMethodMatcher
方法。
publicMethodMatchergetMethodMatcher() { obtainPointcutExpression(); returnthis; }
可以发现,返回的是它本身,而它又是实现了 IntroductionAwareMethodMatcher 接口的。因此,上述的if
判断结果是true
。
回到canApply
方法接着往下看。
Set<Class<?>>classes=newLinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
将当前要匹配的目标类及其实现的接口类型添加到一个事先初始化好的classes
集合当中。
for (Class<?>clazz : classes) { Method[] methods=ReflectionUtils.getAllDeclaredMethods(clazz); for (Methodmethod : methods) { if (introductionAwareMethodMatcher!=null?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { returntrue; } } } returnfalse;
遍历classes
中的每一个类型中声明的每一个方法,通过切入点的方法匹配器的matches
进行匹配,只要有一个方法与切入点匹配,则代表当前处理的目标类是需要创建 AOP 代理的,就返回true
,如果一个方法都没匹配到,则在方法的末尾返回false
。
至此,介绍完以上的流程,findEligibleAdvisors
方法的逻辑就介绍完了。
总结
本文介绍了 Spring 通过findEligibleAdvisors
查找与目标类匹配的 Advisor 的原理,其中包含类级别的匹配和方法的匹配,只要 Bean 实例的类中有一个方法被匹配到,那么,Bean 就需要被代理。