Spring 源码阅读 66:基于 JDK 的 AOP 代理如何获取拦截器链(4)- 将 Advice 封装为拦截器

简介: 【1月更文挑战第1天】本文分析了 Advice 被封装成 MethodInterceptor 的过程,Spring AOP 用到的五种 Advice 中,有些本身就是 MethodInterceptor 的实现类,而有些需要通过适配器的封装。


基于 Spring Framework v5.2.6.RELEASE

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

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

概述

上一篇分析了从 Advisor 中获取 Advice 的过程,在得到 Advice 对象之后,还需要将它封装成拦截器,才能最终交给 JdkDynamicAopProxy 的invoke回调方法来执行其中的增强逻辑。本文的主要内容就是分析如何将 Advice 封装成拦截器,为了内容连贯性,建议从之前的文章开始阅读。

封装拦截器

先从 DefaultAdvisorAdapterRegistry 的getInterceptors方法开始入手。

通过advisorgetAdvice方法获取 Advice 对象的过程,是上一篇文章分析的主要内容,我们接着看后面的内容。

MethodInterceptor 的处理

if (adviceinstanceofMethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}

得到 Advice 之后,首先判断了它是不是 MethodInterceptor 接口的对象。我们看一下 MethodInterceptor 接口的定义。

@FunctionalInterfacepublicinterfaceMethodInterceptorextendsInterceptor {
Objectinvoke(MethodInvocationinvocation) throwsThrowable;
}

它是一个函数式接口,包含了一个invoke方法,方法的参数对象是当前的目标方法调用。再上一步获取 Advice 的过程分析中可以知道,我们的到的 Advice 一共就 5 中,分别对应了切面配置中的五种增强类型,所以,这里可以看一下这五种类型的 Advice 哪些是实现了 MethodInterceptor 接口的。

从上面的类关系图中可以看到,AspectJAroundAdvice / AspectJAfterAdvice / AspectJAfterThrowingAdvice 三个类型的 Advice 是实现了 MethodInterceptor 接口的,因此,三个类型中也包含了invoke方法的实现,而其他两个没有。

结合getInterceptors方法的返回值类型是MethodInterceptor[],可以知道,实现了 MethodInterceptor 接口的 Advice 可以直接作为返回值数组中的元素,添加到结果数组中,因此,代码中也是这样做的,在if语句块中将其添加到了方法开头声明好的列表interceptors中。

非 MethodInterceptor 的处理

再看下一段逻辑。

for (AdvisorAdapteradapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
   }
}

先看一下for循环条件语句中的this.adapters是什么。

privatefinalList<AdvisorAdapter>adapters=newArrayList<>(3);
publicDefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(newMethodBeforeAdviceAdapter());
registerAdvisorAdapter(newAfterReturningAdviceAdapter());
registerAdvisorAdapter(newThrowsAdviceAdapter());
}

结合成员变量的初始化,和构造方法中的逻辑可以看到,adapters中包含了 3 个对象,从名字可以看出他们是 Advice 相关的适配器。

从类关系图中可以看到,它们都是 AdvicorAdapter 的实现类,接口中声明了两个方法,通过接口源码中的注释,可以大概了解这两个方法的作用。

  • supportsAdvice 方法用来判断,当前的 AdvisorAdapter 类型,是否适配参数中给定的 Advice。
  • getInterceptor 的作用是通过给定的 Advisor 对象,获取一个包含 Advisor 中增强逻辑的 MethodInterceptor。

这是一个很典型的适配器模式的逻辑。通过它们的supportsAdvice方法可以知道它们适配的 Advice 类型分别是什么。

  • MethodBeforeAdviceAdapter 适配 MethodBeforeAdvice
  • AfterReturningAdviceAdapter 适配 AfterReturningAdvice
  • ThrowsAdviceAdapter 适配 ThrowsAdvice

在五个增强类型对应的 Advice 类型中,只有两个属于上述被适配的类型,分别是:

  • AspectJMethodBeforeAdvice 实现了 MethodBeforeAdvice 接口
  • AspectJAfterReturningAdvice 实现了 AfterReturningAdvice 接口

而这两个正好是没有实现 MethodInterceptor 接口的两个,也就是说,通过这一步操作,所有类型的 Advice 就都处理完了。

再回到刚刚的代码中,如果adapters中的一个元素能够适配当前的 Advice 对象,那么通过getInterceptor方法得到的 MethodInterceptor 对象,会被添加到最终结果列表interceptors中。

AdviceAdapter 适配器的原理

因为适配器的工作原理都相似,因此,下面以其中之一 MethodBeforeAdviceAdapter 来分析其工作原理。

// org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter#supportsAdvice@OverridepublicbooleansupportsAdvice(Adviceadvice) {
return (adviceinstanceofMethodBeforeAdvice);
}

supportsAdvice方法的原理比较简单,就是判断当前要处理的 Advice 类型,是不是特定的 Advice 类型,如果是的话,表示当前的适配器能够适配这个类型的 Advice 对象。它主要的工作还是在getInterceptor方法中完成的。

// org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter#getInterceptor@OverridepublicMethodInterceptorgetInterceptor(Advisoradvisor) {
MethodBeforeAdviceadvice= (MethodBeforeAdvice) advisor.getAdvice();
returnnewMethodBeforeAdviceInterceptor(advice);
}

在getInterceptor方法中,先获取到 Advisor 的getAdvice方法。在上一篇分析中,我们知道了,这里的getAdvice方法是有缓存机制的,因此不会再将封装 Advice 的过程又执行一遍,而是从缓存中读取。得到的advice是 MethodBeforeAdvice 类型,也就是通过supportsAdvice判断过适配的类型。然后再将其封装成一个 MethodBeforeAdviceInterceptor 返回。

我们看一下 MethodBeforeAdviceInterceptor 的定义。

publicclassMethodBeforeAdviceInterceptorimplementsMethodInterceptor, BeforeAdvice, Serializable {
privatefinalMethodBeforeAdviceadvice;
publicMethodBeforeAdviceInterceptor(MethodBeforeAdviceadvice) {
Assert.notNull(advice, "Advice must not be null");
this.advice=advice;
   }
@OverridepublicObjectinvoke(MethodInvocationmi) throwsThrowable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
returnmi.proceed();
   }
}

可以看到 MethodBeforeAdviceInterceptor 是实现了 MethodInterceptor 接口的。其实它的作用就是对 MethodBeforeAdvice 进行封装,得到一个 MethodInterceptor。

分析完了 MethodBeforeAdviceInterceptor 之后,AfterReturningAdviceAdapter 的逻辑是相似的,就不重复分析了,唯一的区别就是invoke方法中的实现不同,分别对应的不同类型的增强逻辑的执行时机不同。

返回结果

以上,就走完了getInterceptors方法的所有流程,得到一个 MethodInterceptor 的数组,结合前几篇文章分析的获取拦截器链的过程,这些 MethodInterceptor 会最终返回到 JdkDynamicAopProxy 的invoke方法中,作为以下这样代码的结果。

// Get the interception chain for this method.List<Object>chain=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

也就是这行代码所调用的获取拦截器链的逻辑,一共写了四篇分析,欢迎阅读。

总结

本文分析了 Advice 被封装成 MethodInterceptor 的过程,Spring AOP 用到的五种 Advice 中,有些本身就是 MethodInterceptor 的实现类,而有些需要通过适配器的封装。至此,查找拦截器链的过程分析完了,接下来会分析 JdkDynamicAopProxy 的invoke方法的下一个重要逻辑,就是增强逻辑的执行。

目录
相关文章
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
21小时前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
21 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
7天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
15 2
|
7天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
20 2
|
12天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
24 5
|
12天前
|
XML Java 数据格式
Spring AOP
【5月更文挑战第1天】Spring AOP
27 5
|
13天前
|
Java 编译器 开发者
Spring的AOP理解
Spring的AOP理解
|
13天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
17 0
|
13天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
22 0
|
13天前
|
Java 测试技术 开发者
【亮剑】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。