Spring 源码阅读 52:查找注解配置的切面增强逻辑(2)- 查找增强方法
基于 Spring Framework v5.2.6.RELEASE
概述
对于 Spring AOP 中通过注解配置的切面信息,AnnotationAwareAspectJAutoProxyCreator 后处理器类中的findCandidateAdvisors
方法用来从 Spring 容器中找到所有的切面配置类,并找到其中的增强逻辑。
在上一篇中,重点分析了findCandidateAdvisors
方法中调用的buildAspectJAdvisors
方法,如何将符合条件的切面配置类找到,本文我们讲分析如何从这些配置类中,将其中的增强逻辑找出来。作为上一篇后续的内容,本文的分析从buildAspectJAdvisors
方法中调用的getAdvisors
方法作为分析的切入点。
查找增强方法
调用getAdvisors
方法是传入的参数,是一个包含了当前 Spring 容器和切面配置类的 Bean 名称的 MetadataAwareAspectInstanceFactory 对象。
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
getAdvisors 方法
进入getAdvisors
方法。
方法实现在 ReflectiveAspectJAdvisorFactory类中,代码的结构比较清晰,我们还是分布来进行分析。
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
首先获取了切面配置类的类型信息和 Bean 名称,并对类型信息进行了验证。验证的目的主要是为了保证它是一个标注了@``Aspect
注解的非抽象类型,切与切面相关的配置信息是符合后续处理的条件的。
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
然后用 MetadataAwareAspectInstanceFactory 对参数传入的aspectInstanceFactory进行包装,确保它只会被实例化一次。
接下来进入关键的逻辑。
getAdvisorMethods 方法
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
通过getAdvisorMethods
从切面配置类的类信息中获取一个 Method 列表,从方法名字看,获取到的应该是增强方法的列表。
我们进入getAdvisorMethods
查看一下。
// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
ReflectionUtils.doWithMethods(aspectClass, method -> {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.size() > 1) {
methods.sort(METHOD_COMPARATOR);
}
return methods;
}
这里面还涉及到了 ReflectionUtils 类中的doWithMethods
方法,我们先这个方法。
// org.springframework.util.ReflectionUtils#doWithMethods(java.lang.Class<?>, org.springframework.util.ReflectionUtils.MethodCallback, org.springframework.util.ReflectionUtils.MethodFilter)
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
Method[] methods = getDeclaredMethods(clazz, false);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue;
}
try {
mc.doWith(method);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}
else if (clazz.isInterface()) {
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}
}
这个方法的参数列表中,除了一个 Class 类型信息之外,还有两个参数,分别是 MethodCallback 和 MethodFilter 类型,这两个类型的源码就不贴在这里了,他们其实都是函数式接口,从名称中也可以看出它们的用途,后续的方法体中遇到了我们再深入分析。
在doWithMethods
方法中,首先获取到了类型中声明的所有方法,得到一个 Method 列表,然后遍历这个列表。在for循环中,首先通过 MethodFilter 的matches
方法对其进行了过滤,然后将方法信息作为参数,调用了MethodCallback 的doWith
方法。
我们看一下这里的matches
和doWith
的具体实现。
matches
方法对应上一步调用doWithMethods
方法的第三个参数,也就是ReflectionUtils.USER_DECLARED_METHODS
,找到这个常量的定义。
public static final MethodFilter USER_DECLARED_METHODS =
(method -> !method.isBridge() && !method.isSynthetic());
可以看到,这里的逻辑就是过滤掉桥接方法和合成方法,可以简单理解为只筛选出我们通过写代码声明的方法。
过滤掉不需要处理的方法之后,再执行doWith
方法,doWith
方法的实现,其实就是在doWithMethods
方法中传入的 Lambda 函数,其逻辑就是将没有标记@``Pointcut
注解的方法信息添加到事先创建的methods
集合中。
此外,在doWithMethods
方法的最后,还会递归处理当前类型的父类。
回到getAdvisorMethods
方法中,当doWithMethods
执行完之后,对获取到的methods
进行排序,就得到了方法最重要返回的结果。
总结一下,getAdvisorMethods
的原理就是,将指定类中所有声明的方法,过滤掉桥接方法、合成方法,以及标记了@``Pointcut
注解的方法(也就是定义切入点的方法),将剩下的方法进行排序得到结果。
创建增强方法对应的 Advisor
再回到getAdvisors
方法中,接下来会遍历上一步得到的methods
集合。
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
这里的逻辑非常简单,就是使用遍历到的方法信息method
创建其对应的 Advisor,每一个增强方法都会对应一个 Advisor,这些 Advisor 会被添加到事先创建的advisors
集合中。
这里的getAdvisor
方法是一个关键的步骤,它的作用就是将增强方法封装成一个 Advisor 对象。这里面的内容比较到,限于篇幅,我打算单开一篇进行分析。本文中,我们先把getAdvisors
方法后续的逻辑分析完。
后续的处理
以下是getAdvisors
方法中后续的代码。
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
这里主要执行的逻辑是:
- 如果这个切面配置是延迟初始化的, 那么在
advisors
集合的开头添加一个 SyntheticInstantiationAdvisor。 - 根据切面配置类中标记了
@DeclareParents
注解的字段,生成相应的 DeclareParentsAdvisor 并添加到advisors
集合中。 - 将最终的
advisors
集合返回。
再返回最终结果之前的两步操作很少会用到,因此不做深入分析了。
总结
本文分析了在获取到基于注解的切面配置类之后,如何从中找到配置增强逻辑的方法,找到后,Spring 会为每一个增强逻辑创建一个 Advisor。下一篇,将深入分析 Advisor 创建的原理。
转载自:https://juejin.cn/post/7156984744134049806