Spring 源码阅读 65:基于 JDK 的 AOP 代理如何获取拦截器链(3)- 从 Advisor 获取 Advice

简介: 本文主要分析了 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice方法,如何从 Advisor 中获取 Advice 对象的过程。

本文主要分析了 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice方法,如何从 Advisor 中获取 Advice 对象的过程。

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

接上一篇:Spring 源码阅读 64:基于 JDK 的 AOP 代理如何获取拦截器链(2)- Advisor 与目标方法的匹配

概述

上一篇介绍了如何为被调用的目标方法匹配 Advisor,匹配到的 Advisor 会通过 DefaultAdvisorAdapterRegistry 的getInterceptors方法会去到对应的拦截器,并添加到最终的拦截器链中。本文来深入分析getInterceptors方法。

获取 Advice

以下是定义在 DefaultAdvisorAdapterRegistry 类中的getInterceptors方法源码。

// org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors@OverridepublicMethodInterceptor[] getInterceptors(Advisoradvisor) throwsUnknownAdviceTypeException {
List<MethodInterceptor>interceptors=newArrayList<>(3);
Adviceadvice=advisor.getAdvice();
if (adviceinstanceofMethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
   }
for (AdvisorAdapteradapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
      }
   }
if (interceptors.isEmpty()) {
thrownewUnknownAdviceTypeException(advisor.getAdvice());
   }
returninterceptors.toArray(newMethodInterceptor[0]);
}

方法的开头声明了一个 MethodInterceptor 类型的列表interceptors,它会在方法的结尾被转换为数组后作为结果返回。

然后,调用了advisorgetAdvice方法。这里需要留意一下,Advisor 是一个接口,getAdvice方法是在 Advisor 接口中定义的方法,Advisor 的实现类并非都有advice属性。结合以上两点,所以这里的getAdvice方法不能被看成一个普通的 getter 方法。

要找到getAdvice方法的逻辑首先要知道这里的 Advisor 具体是什么类型。

Advisor 的类型

我们之前分析过了加载切面配置和查找 Advisor 的代码,从中可以知道 Advisor 的具体类型,这里做一个简单的回顾。

如果是通过 XML 配置文件进行的切面配置,那么,在加载配置的时候,创建的 Advisor 类型是 AspectJPointcutAdvisor,其中有一个advice属性,在创建 AspectJPointcutAdvisor 对象的时候会作为构造方法的参数传入,因此,这种情况下可以直接通过getAdvice方法获取到 Advice 对象。

参考:Spring 源码阅读 50:解析 XML 中的切面配置

而如果是通过注解的方式配置的切面,Advisor 的类型是 InstantiationModelAwarePointcutAdvisorImpl,其中并没有 Advice 类型的属性,当它的getAdvice方法被调用的时候,才会封装 Advice。

参考:Spring 源码阅读 53:查找注解配置的切面增强逻辑(3)- 创建 Advisor

下面看一下具体封装的逻辑。

获取 Advice

找到 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice方法。

// org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#getAdvice@OverridepublicsynchronizedAdvicegetAdvice() {
if (this.instantiatedAdvice==null) {
this.instantiatedAdvice=instantiateAdvice(this.declaredPointcut);
   }
returnthis.instantiatedAdvice;
}

主要的逻辑由instantiateAdvice方法完成。

// org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#instantiateAdviceprivateAdviceinstantiateAdvice(AspectJExpressionPointcutpointcut) {
Adviceadvice=this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice!=null?advice : EMPTY_ADVICE);
}

再进入aspectJAdvisorFactorygetAdvice方法,这个方法的具体实现在 ReflectiveAspectJAdvisorFactory 类型中。

这个方法代码比较多,但是并不复杂。

Class<?>candidateAspectClass=aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);

首先,获取并验证了candidateAspectClass,也就是配置切面的类。

AspectJAnnotation<?>aspectJAnnotation=AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation==null) {
returnnull;
}

然后,在方法信息上查找切面配置相关的注解,findAspectJAnnotationOnMethod方法的源码如下:

// org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotation@NullableprotectedstaticAspectJAnnotation<?>findAspectJAnnotationOnMethod(Methodmethod) {
for (Class<?>clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?>foundAnnotation=findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation!=null) {
returnfoundAnnotation;
      }
   }
returnnull;
}

逻辑比较简单,就是遍历ASPECTJ_ANNOTATION_CLASSES中的类型信息,然后在方法上查找该类型的注解,如果找到了,就直接返回结果。ASPECTJ_ANNOTATION_CLASSES包含的注解类型就是切面配置类中可以在方法上标记的几个注解类型。

privatestaticfinalClass<?>[] ASPECTJ_ANNOTATION_CLASSES=newClass<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

回到getAdvice中,如果在方法上找不到这些注解,那么说明这个方法与切面的配置无关,则直接返回空。

接着往下看。

// If we get here, we know we have an AspectJ method.// Check that it's an AspectJ-annotated classif (!isAspect(candidateAspectClass)) {
thrownewAopConfigException("Advice must be declared inside an aspect type: "+"Offending method '"+candidateAdviceMethod+"' in class ["+candidateAspectClass.getName() +"]");
}

到这里,其实已经找到了方法上的注解,这一步是为了确保这个方法是在一个切面配置类中。以上这些都是准备工作。

下面开始创建 Advice,这个过程对应方法体中switch语句的部分。其中,针对方法上的注解,分别做了不同的处理。我们依次分析。

对于 PointCut 注解,这里不需要处理,因为它标记的方法并不是一个增强方法。

对于 Around 注解,会创建一个 AspectJAroundAdvice 类型的 Advice 对象。我们进入构造方法查看。

publicAspectJAroundAdvice(
MethodaspectJAroundAdviceMethod, AspectJExpressionPointcutpointcut, AspectInstanceFactoryaif) {
super(aspectJAroundAdviceMethod, pointcut, aif);
}

直接调用了父类 AbstractAspectJAdvice 的构造方法。

publicAbstractAspectJAdvice(
MethodaspectJAdviceMethod, AspectJExpressionPointcutpointcut, AspectInstanceFactoryaspectInstanceFactory) {
Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
this.declaringClass=aspectJAdviceMethod.getDeclaringClass();
this.methodName=aspectJAdviceMethod.getName();
this.parameterTypes=aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod=aspectJAdviceMethod;
this.pointcut=pointcut;
this.aspectInstanceFactory=aspectInstanceFactory;
}

构造方法中也没有复杂的逻辑,就是一些成员变量的初始化。构造完的 AspectJAroundAdvice 对象会被赋值到springAdvice变量。

对于后面的 Before / After / AfterReturning / AfterThrowing 这四个注解,也都有上述相同的逻辑,它们都会通过构造方法初始化,而构造方法中也都调用了父类 AbstractAspectJAdvice 的构造方法,因此,它们的创建逻辑相同,只是最终的对象类型不同而已。

不过 AfterReturning 和 AfterThrowing 注解在创建完之后,还给springAdvice设置了额外的属性,这个额外的属性分别对应 AfterReturning 注解的returning属性以及 AfterThrowing 注解的throwing属性,这样,配置在注解上的属性值,就会保存在最终创建的 Advice 对象中。

最后还有一种情况,就是不属于以上任何注解的情况,会抛出异常。

现在就得到了一个 Advice 对象springAdvice

配置 Advice

// Now to configure the advice...springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames=this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames!=null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
returnspringAdvice;

方法返回结果之前,再对springAdvice对象做一些必要的配置,包括aspectNamedeclarationOrder两个属性的设置,以及增强方法的参数绑定。

增强方法的参数中可以包含一个 JoinPoint 类型的参数,表示当前被增强的接入点,对于 AfterReturning 和 AfterThrowing 类型的增强方法,还可以在方法参数中包含一个表示当前目标方法调用的异常或者返回值的参数,然后在注解的对应属性上指定相应的参数名,这里就会对这些参数进行绑定,使得增强方法中可以获取到目标方法跑出的一场或者返回的结果。

最后将springAdvice返回即可。

至此,InstantiationModelAwarePointcutAdvisorImpl 的getAdvice方法的流程就分析完了。

回到 DefaultAdvisorAdapterRegistry 的getInterceptors方法中。

// org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors@OverridepublicMethodInterceptor[] getInterceptors(Advisoradvisor) throwsUnknownAdviceTypeException {
List<MethodInterceptor>interceptors=newArrayList<>(3);
Adviceadvice=advisor.getAdvice();
if (adviceinstanceofMethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
   }
for (AdvisorAdapteradapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
      }
   }
if (interceptors.isEmpty()) {
thrownewUnknownAdviceTypeException(advisor.getAdvice());
   }
returninterceptors.toArray(newMethodInterceptor[0]);
}

第一步获取 Advice 的过程就分析完了,后面的流程就是将 Advice 对象,封装成拦截器。

总结

本文主要分析了 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice方法,如何从 Advisor 中获取 Advice 对象的过程。对于通过注解配置的切面信息中的增强方法,其对应的 Advice 对象需要在获取的时候进行创建,不过这个创建的过程只会执行一次,之后会被缓存在 InstantiationModelAwarePointcutAdvisorImpl 的instantiatedAdvice成员变量中。

下一篇讲分析getInterceptors方法之后的流程,如何将 Advice 对象封装成对应的拦截器。

目录
相关文章
|
2月前
|
安全 前端开发 Java
JDK源码级别彻底剖析JVM类加载机制
JDK源码级别彻底剖析JVM类加载机制
|
3月前
|
Java Spring
Spring5源码(38)-SpringAop代理调用过程(二)
Spring5源码(38)-SpringAop代理调用过程(二)
29 0
|
3月前
|
Java Spring
Spring5源码(37)-SpringAop代理调用过程(一)
Spring5源码(37)-SpringAop代理调用过程(一)
23 0
|
3月前
|
缓存 Java Spring
Spring5源码(35)-SpringAop创建代理(一)
Spring5源码(35)-SpringAop创建代理(一)
22 0
|
18天前
|
前端开发 Java Spring
[AIGC] Spring Interceptor 拦截器详解
[AIGC] Spring Interceptor 拦截器详解
|
2月前
|
缓存 前端开发 Java
【Spring底层原理高级进阶】轻松掌握 Spring MVC 的拦截器机制:深入理解 HandlerInterceptor 接口和其实现类的用法
【Spring底层原理高级进阶】轻松掌握 Spring MVC 的拦截器机制:深入理解 HandlerInterceptor 接口和其实现类的用法
|
2月前
|
算法 Java 索引
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)
35 0
|
3月前
|
设计模式 Java
根据JDK源码Calendar来看工厂模式和建造者模式
根据JDK源码Calendar来看工厂模式和建造者模式
|
3月前
|
设计模式 安全 Java
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
381 1
|
3月前
spring-state-machine拦截器
spring-state-machine拦截器
26 0