持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
概述
查找基于注解的切面配置增强逻辑的源码分析,总共分三篇,这是第三篇,强烈建议按顺序阅读。
前面我们分析了 Spring 如何从容器中找到切面配置类对应的 Bean,以及从中找到配置增强逻辑的方法,本文将分析最后一个关键的步骤,就是如何将找到的增强方法封装成 Advisor。
创建 Advisor
作为上一篇的后续,本文的分析从 ReflectiveAspectJAdvisorFactory 的getAdvisor方法入手。
// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisor
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
首先对增强方法所在的切面配置类进行验证,然后通过getPointcut获取增强方法对应的切入点信息。之后,在确保能够获取到切入点信息的情况下,通过构造方法创建 Advisor 对象,这里的 Advisor 对象类型是 InstantiationModelAwarePointcutAdvisorImpl 类型,其中包含了切入点信息、增强方法、切面配置类的 Bean 名称等重要信息。
因此,这里的重点有两个,一个是通过getPointcut方法获取切入点信息,另一个就是 Advisor 的创建,我们分别来看。
获取切入点信息
我们先进入getPointcut方法。
// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getPointcut
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
首先,会通过 AbstractAspectJAdvisorFactory 类的findAspectJAnnotationOnMethod方法获取方法上标记的增强注解信息,我们进入这个方法看一下。
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
其中的常量定义如下,也就是@Pointcut以及五种增强类型对应的注解。
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
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 对象并返回。
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
这里创建 Advisor 的方式是通过构造方法创建,它的具体类型是 InstantiationModelAwarePointcutAdvisorImpl,我们现通过继承关系了解一下这个类。
先对它的继承关系大致有个印象,然后我们看构造方法中的逻辑。
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
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.
Pointcut preInstantiationPointcut = 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 = new PerTargetInstantiationModelPointcut(
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(本文)