基于 Spring Framework v5.2.6.RELEASE
概述
查找基于注解的切面配置增强逻辑的源码分析,总共分三篇,这是第三篇,强烈建议按顺序阅读。
前面我们分析了 Spring 如何从容器中找到切面配置类对应的 Bean,以及从中找到配置增强逻辑的方法,本文将分析最后一个关键的步骤,就是如何将找到的增强方法封装成 Advisor。
创建 Advisor
作为上一篇的后续,本文的分析从 ReflectiveAspectJAdvisorFactory 的getAdvisor
方法入手。
// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorpublicAdvisorgetAdvisor(MethodcandidateAdviceMethod, MetadataAwareAspectInstanceFactoryaspectInstanceFactory, intdeclarationOrderInAspect, StringaspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); AspectJExpressionPointcutexpressionPointcut=getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut==null) { returnnull; } returnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
首先对增强方法所在的切面配置类进行验证,然后通过getPointcut
获取增强方法对应的切入点信息。之后,在确保能够获取到切入点信息的情况下,通过构造方法创建 Advisor 对象,这里的 Advisor 对象类型是 InstantiationModelAwarePointcutAdvisorImpl 类型,其中包含了切入点信息、增强方法、切面配置类的 Bean 名称等重要信息。
因此,这里的重点有两个,一个是通过getPointcut
方法获取切入点信息,另一个就是 Advisor 的创建,我们分别来看。
获取切入点信息
我们先进入getPointcut
方法。
// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getPointcutprivateAspectJExpressionPointcutgetPointcut(MethodcandidateAdviceMethod, Class<?>candidateAspectClass) { AspectJAnnotation<?>aspectJAnnotation=AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation==null) { returnnull; } AspectJExpressionPointcutajexp=newAspectJExpressionPointcut(candidateAspectClass, newString[0], newClass<?>[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); if (this.beanFactory!=null) { ajexp.setBeanFactory(this.beanFactory); } returnajexp; }
首先,会通过 AbstractAspectJAdvisorFactory 类的findAspectJAnnotationOnMethod
方法获取方法上标记的增强注解信息,我们进入这个方法看一下。
protectedstaticAspectJAnnotation<?>findAspectJAnnotationOnMethod(Methodmethod) { for (Class<?>clazz : ASPECTJ_ANNOTATION_CLASSES) { AspectJAnnotation<?>foundAnnotation=findAnnotation(method, (Class<Annotation>) clazz); if (foundAnnotation!=null) { returnfoundAnnotation; } } returnnull; }
其中的常量定义如下,也就是@
Pointcut
以及五种增强类型对应的注解。
privatestaticfinalClass<?>[] ASPECTJ_ANNOTATION_CLASSES=newClass<?>[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
在findAspectJAnnotationOnMethod
方法中,会遍历这些注解类,对于每一个遍历到的注解类型,如果发现给定的方法上标记了这个注解,那么就返回封装好的 AspectJAnnotation 注解信息,如果都没有匹配到,则返回空。
这个逻辑中还隐含了一个逻辑,如果一个注解在给定的方法上找到了,那么后面就不再判断了。
回到getPointcut方法中,如果上一步没有获取到注解信息,那么就说明当前要处理的方法不是一个增强方法,就不再处理了。
接下来,创建一个 AspectJExpressionPointcut 对象ajexp
,将切面类的类型、增强注解中的切入点表达式、当前的 Spring 容器等赋值到ajexp
的对应属性,最后,将它作为getPointcut
方法的结果返回。
至此,getPointcut
方法就结束了,最终返回一个 AspectJExpressionPointcut 类型的结果或者一个空值。我们接着看getAdvisor
方法的另外一个关键步骤。
创建 Advisor 对象
如果上一步获取的切入点信息不为空的话,则会创建 Advisor 对象并返回。
returnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
这里创建 Advisor 的方式是通过构造方法创建,它的具体类型是 InstantiationModelAwarePointcutAdvisorImpl,我们现通过继承关系了解一下这个类。
先对它的继承关系大致有个印象,然后我们看构造方法中的逻辑。
publicInstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcutdeclaredPointcut, MethodaspectJAdviceMethod, AspectJAdvisorFactoryaspectJAdvisorFactory, MetadataAwareAspectInstanceFactoryaspectInstanceFactory, intdeclarationOrder, StringaspectName) { this.declaredPointcut=declaredPointcut; this.declaringClass=aspectJAdviceMethod.getDeclaringClass(); this.methodName=aspectJAdviceMethod.getName(); this.parameterTypes=aspectJAdviceMethod.getParameterTypes(); this.aspectJAdviceMethod=aspectJAdviceMethod; this.aspectJAdvisorFactory=aspectJAdvisorFactory; this.aspectInstanceFactory=aspectInstanceFactory; this.declarationOrder=declarationOrder; this.aspectName=aspectName; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type.PointcutpreInstantiationPointcut=Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.// If it's not a dynamic pointcut, it may be optimized out// by the Spring AOP infrastructure after the first evaluation.this.pointcut=newPerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy=true; } else { // A singleton aspect.this.pointcut=this.declaredPointcut; this.lazy=false; this.instantiatedAdvice=instantiateAdvice(this.declaredPointcut); } }
其实并没有什么复杂的逻辑,就是将参数传入的各项信息复制给了对应的成员变量。
到这里,getAdvisor
方法的原理也就分析完了。
findCandidateAdvisors 方法小结
经过三篇文章的分析,我们已经了解了 AnnotationAwareAspectJAutoProxyCreator 类中的findCandidateAdvisors
方法作为被后处理器调用的方法,如何从当前容器中查找到所有的切面配置类,然后再从这些类中找到所有的增强方法并封装成对应的 Advisor 对象返回。
为了让后续的分析更加连贯,我们再回顾一下之前的内容。
- 我们最初从 AbstractAuthProxyCreator 类的
wrapIfNecessary
方法为入口,分析 Spring 如何在后处理器中为已经完成初始化的 Bean 实例创建 AOP 代理。 - 在
wrapIfNecessary
方法中,会通过getAdvicesAndAdvisorsForBean
方法获取能够对当前 Bean 实例进行增强的 Advisor,这个工作又被交给了findEligibleAdvisors
方法。 - 而
findEligibleAdvisors
方法中的第一步,就是获取到容器中所有能获取到的 Advisor。
前面的几篇文章,就是在分析如何从容器中获取到全部的 Advisor,我会将最近的几篇文章链接放在文章末尾以供回顾。下一篇讲开始分析之后的内容,也就是如何从全部的 Advisor 中找到与当前处理的 Bean 实例匹配的 Advisor。
总结
本文分析了 Spring 查找注解配置的 AOP 切面信息的最后一部分,Spring 会将查找到的所有增强类中的方法进行筛选,找到所有的增强方法,并将其封装成对应的 Advisor。
相关阅读:
- Spring 源码阅读 50:解析 XML 中的切面配置
- Spring 源码阅读 51:查找注解配置的切面增强逻辑(1)- 查找配置类
- Spring 源码阅读 52:查找注解配置的切面增强逻辑(2)- 查找增强方法
- Spring 源码阅读 53:查找注解配置的切面增强逻辑(3)- 创建 Advisor (本文)