基于 Spring Framework v5.2.6.RELEASE
概述
在【Spring 源码阅读 49:AOP 代理创建的过程 】一文中,我们分析了 AOP 代理创建的大致流程,其中提到了,无论是通过 XML 还是注解配置的切面,它们的工作原理都是大致一样的,其中有一个比较大的区别就是在增强的查找方面。
在上一篇中,我们分析了通过 XML 配置切面的情况下,切面配置信息是如何被 Spring 从 XML 文件中解析并加载的,以及负责创建 AOP 的后处理器是如何在处理逻辑中找到它们的。与之不同的是,通过注解配置的切面信息会是以一个 Java 类为载体的,而这个类会作为一个普通的 Bean 被注册到 Spring 容器中。
这篇文章,我们分析基于注解配置的切面信息,是如何被创建 AOP 的后处理器找到的。
查找过程
与上一篇一样,我们还是从findCandidateAdvisors
方法入手,在基于注解配置切面的情况下,负责创建 AOP 代理的后处理器是 AnnotationAwareAspectJAutoProxyCreator,它自己重写了父类的findCandidateAdvisors
方法,我们找到其实现方法。
protectedList<Advisor>findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules.List<Advisor>advisors=super.findCandidateAdvisors(); // Build Advisors for all AspectJ aspects in the bean factory.if (this.aspectJAdvisorsBuilder!=null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } returnadvisors; }
父类的 findCandidateAdvisors 方法
首先,它调用了父类的findCandidateAdvisors
方法,它的父类是 AspectJAwareAdvisorAutoProxyCreator,其中并没有这个方法的实现,而父类的父类 AbstractAdvisorAutoProxyCreator 有这个方法的实现逻辑。因此,这里调用了 AbstractAdvisorAutoProxyCreator 的findCandidateAdvisors
方法。
而 AbstractAdvisorAutoProxyCreator 的findCandidateAdvisors
方法,就是我们在上一篇【Spring 源码阅读 50:解析 XML 中的切面配置 】中分析过的用于查找 XML 配置的切面信息的方法。因此,这里的逻辑是先查找 XML 配置的切面信息,得到一个 Advisor 类型的集合advisors
。
接下来,通过成员变量aspectJAdvisorsBuilder
的buildAspectJAdvisors
方法,获取一个 Advisor 列表,添加到之前的advisors
集合中,最后将advisors
作为方法的结果返回。
所以,接下来的重点就是分析buildAspectJAdvisors
方法。
buildAspectJAdvisors 方法
我们找到方法的源码。
从方法的注释中可以看到,这个方法的作用就是在容器中找到所有基于 Aspect 注解配置的切面信息,并为每一个增强方法创建一个 Advisor,以列表的形式返回结果。
这个方法比较长,我们还是分段来分析。
List<String>aspectNames=this.aspectBeanNames; if (aspectNames==null) { synchronized (this) { aspectNames=this.aspectBeanNames; if (aspectNames==null) { // 省略中间的部分。。。 } } }
首先会从成员变量aspectBeanNames
中获取所有切面 Bean 得名称,如果是第一次走到这个方法,这里的列表肯定是空的,此时会进入if
语句块的逻辑。进入if
语句块中。
List<Advisor>advisors=newArrayList<>(); aspectNames=newArrayList<>(); String[] beanNames=BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false);
首先定义了一个 Advisor 列表,并初始化了aspectNames
列表,然后通过 BeanFactoryUtils 的beanNamesForTypeIncludingAncestors
方法,获取到了容器中所有的 Bean 名称,得到beanNames
数组。
for (StringbeanName : beanNames) { if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they// would be cached by the Spring container but would not have been weaved.Class<?>beanType=this.beanFactory.getType(beanName); if (beanType==null) { continue; } if (this.advisorFactory.isAspect(beanType)) { // 省略部分语句 } }
接下来,遍历上一步获取到的beanNames
数组。
对于每一个beanName
,先通过isEligibleBean
方法筛选,这个方法默认返回true
,因此不会执行continue
。然后,判断它对应的 Bean 的类型不为空。
在第三个if
判断条件中,通过isAspect
方法判断了当前遍历到的beanName
对应的类型是不是一个切面配置的类,我们可以进入isAspect
方法,看一下具体的判断逻辑。
// org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspectpublicbooleanisAspect(Class<?>clazz) { return (hasAspectAnnotation(clazz) &&!compiledByAjc(clazz)); } // org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#hasAspectAnnotationprivatebooleanhasAspectAnnotation(Class<?>clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) !=null); }
其实逻辑很简单,即使看这个类型是不是被标记了@
Aspect
注解。
回到前面的方法中,如果此时判断一个类型是切面配置的类型,则执行if
语句块中的逻辑。
if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); AspectMetadataamd=newAspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() ==PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactoryfactory=newBeanFactoryAspectInstanceFactory(this.beanFactory, beanName); List<Advisor>classAdvisors=this.advisorFactory.getAdvisors(factory); if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this.if (this.beanFactory.isSingleton(beanName)) { thrownewIllegalArgumentException("Bean with name '"+beanName+"' is a singleton, but aspect instantiation model is not singleton"); } MetadataAwareAspectInstanceFactoryfactory=newPrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } }
首先,将当前的beanName
添加到aspectNames
集合中,然后使用aspectNames
和beanType
创建一个 AspectMetadata 类型的切面元信息对象amd
。
接下来,通过amd
判断切面的实例化模型,通常情况下都是单例的,也就是会进入if-else
语句块的if
部分。其中,会创建一个 MetadataAwareAspectInstanceFactory 的factory
,然后通过advisorFactory
的getAdvisors
方法,获取包含当前类型的所有增强方法对应的 Advisor 组成的列表。然后判断当前遍历到的类型是不是单例的,如果是,就将获取到的 Advisor 列表添加到advisorsCache
缓存中,如果不是,就将工厂对象factory
添加到aspectFactoryCache
缓存中。
另外,对于当前类型的切面实例化模型不是单例的情况,则不管当前类型是否单例,都将其工厂对象factory
添加到aspectFactoryCache
缓存中。
在此期间,所有获得到的 Advisor 都会被添加到advisors
集合中。
for (StringbeanName : beanNames) { // 省略中间部分。。。} this.aspectBeanNames=aspectNames; returnadvisors;
在结束for
循环后,advisors
会作为buildAspectJAdvisors
方法的结果返回。
getAdvisors 方法
以上的逻辑中,我们还需要深入分析一下getAdvisors
方法,是如何从配面配置类的信息中获取到 Advisor 列表的。这部分我会放到下一篇再深入分析。
总结
本文分析了用于创建 AOP 代理的后处理器是如何查找基于注解配置的切面信息的,这是前半部分,主要包含切面配置类的查找和解析。下一篇将继续分析,针对每一个切面配置类,怎样从中找到所有的增强逻辑并封装成对应的 Advisor 对象。