76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!

网站建设4年前发布
26 00

下面我会简单介绍一下 AOP 的基础知识,以及使用方法,然后直接对源码进行拆解。,不 BB,上文章目录。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,AOP 的全称是 “Aspect Oriented Programming”,即面向切面编程。,在 AOP 的思想里面,周边功能(比如性能统计,日志,事务管理等)被定义为切面,核心功能和切面功能分别独立进行开发,然后把核心功能和切面功能“编织”在一起,这就叫 AOP。,AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。,上面的解释偏官方,下面用“方言”再给大家解释一遍。,5 种通知的分类:,新建 Louzai 类:,添加 LouzaiAspect 切面:,applicationContext.xml 添加:,程序入口:,输出:,这个示例非常简单,“睡觉” 加了前置和后置通知,但是 Spring 在内部是如何工作的呢?,为了方便大家能更好看懂后面的源码,我先整体介绍一下源码的执行流程,让大家有一个整体的认识,否则容易被绕进去。,整个 Spring AOP 源码,其实分为 3 块,我们会结合上面的示例,给大家进行讲解。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,第一块就是前置处理,我们在创建 Louzai Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中,比如示例中 LouzaiAspect 的所有切面信息。,第二块就是后置处理,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,第三块就是执行切面,通过“责任链 + 递归”,去执行切面。,注意:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!,除了原理部分,上面的知识都不难,下面才是我们的重头戏,让你跟着楼仔,走一遍代码流程。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,这里需要多跑几次,把前面的 beanName 跳过去,只看 louzai。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,进入 doGetBean(),进入创建 Bean 的逻辑。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,主要就是遍历切面,放入缓存。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,这里是重点!敲黑板!!!,到这里,获取切面信息的流程就结束了,因为后续对切面数据的获取,都是从缓存 advisorsCache 中拿到。,下面就对上面的流程,再深入解读一下。,上图的第 2 步,逻辑如下:,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,进入到 getAdvice(),生成切面信息。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,主要就是从缓存拿切面,和 louzai 的方法匹配,并创建 AOP 代理对象。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,进入 doCreateBean(),走下面逻辑。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,这里是重点!敲黑板!!!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,我们先进入第一步,看是如何获取 louzai 的切面列表。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,进入 buildAspectJAdvisors(),这个方法应该有印象,就是前面将切面信息放入缓存 advisorsCache 中,现在这里就是要获取缓存。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,再回到 findEligibleAdvisors(),从缓存拿到所有的切面信息后,继续往后执行。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,有了 louzai 的切面列表,后面就可以开始去创建 AOP 代理对象。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,这里是重点!敲黑板!!!,这里有 2 种创建 AOP 代理对象的方式,我们是选用 Cglib 来创建。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,我们再回到创建代理对象的入口,看看创建的代理对象。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,通过 “责任链 + 递归”,执行切面和方法。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,前方高能!这块逻辑非常复杂!!!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,下面就是“执行切面”最核心的逻辑,简单说一下设计思路:,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,因为我们数组里面只有 3 个对象,所以只会递归 3 次,下面就看这 3 次是如何递归,责任链是如何执行的,设计得很巧妙!,数组的第一个对象是 ExposeInvocationInterceptor,执行 invoke(),注意入参是 CglibMethodInvocation。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,里面啥都没干,继续执行 CglibMethodInvocation 的 process()。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,数组的第二个对象是 MethodBeforeAdviceInterceptor,执行 invoke()。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,数组的第二个对象是 AfterReturningAdviceInterceptor,执行 invoke()。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,执行完上面逻辑,就会退出递归,我们看看 invokeJoinpoint() 的执行逻辑,其实就是执行主方法。,再回到第三次递归的入口,继续执行后面的切面。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,切面执行逻辑,前面已经演示过,直接看执行方法。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,后面就依次退出递归,整个流程结束。,这块代码,我研究了大半天,因为这个不是纯粹的责任链模式。,纯粹的责任链模式,对象内部有一个自身的 next 对象,执行完当前对象的方法末尾,就会启动 next 对象的执行,直到最后一个 next 对象执行完毕,或者中途因为某些条件中断执行,责任链才会退出。,这里 CglibMethodInvocation 对象内部没有 next 对象,全程是通过 interceptorsAndDynamicMethodMatchers 长度为 3 的数组控制,依次去执行数组中的对象,直到最后一个对象执行完毕,责任链才会退出。,这个也属于责任链,只是实现方式不一样,后面会详细剖析,下面再讨论一下,这些类之间的关系。,我们的主对象是 CglibMethodInvocation,继承于 ReflectiveMethodInvocation,然后 process() 的核心逻辑,其实都在 ReflectiveMethodInvocation 中。,ReflectiveMethodInvocation 中的 process() 控制整个责任链的执行。,ReflectiveMethodInvocation 中的 process() 方法,里面有个长度为 3 的数组 interceptorsAndDynamicMethodMatchers,里面存储了 3 个对象,分别为 ExposeInvocationInterceptor、MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor。,注意!!!这 3 个对象,都是继承 MethodInterceptor 接口。,76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖!,然后每次执行 invoke() 时,里面都会去执行 CglibMethodInvocation 的 process()。,是不是听得有些蒙圈?甭着急,我重新再帮你梳理一下。,对象和方法的关系:,可能有同学会说,invoke() 的入参是 MethodInvocation,没错!但是 CglibMethodInvocation 也继承了 MethodInvocation,不信自己可以去看。,执行逻辑:,所以这里设计巧妙的地方,是因为纯粹责任链模式,里面的 next 对象,需要保证里面的对象类型完全相同。,但是数组里面的 3 个对象,里面没有 next 成员对象,所以不能直接用责任链模式,那怎么办呢?就单独搞了一个 CglibMethodInvocation.process(),通过去无限递归 process(),来实现这个责任链的逻辑。,这就是我们为什么要看源码,学习里面优秀的设计思路!,我们再小节一下,文章先介绍了什么是 AOP,以及 AOP 的原理和示例。,之后再剖析了 AOP 的源码,分为 3 块:,最难的地方还不是抠图,而是 “切面执行”的设计思路,虽然流程能走通,但是把整个设计思想能总结出来,并讲得能让大家明白,还是非常不容易的。

© 版权声明

相关文章