从底层分析并详解SpringAOP底层实现

首先分析AOP的实现

首先切面(Advisor)由通知(Advice)和切点(Pointcut)组成

包括前置通知后置通知等等最终都会被转化为实现 MethodInterceptor 接口的环绕通知

先看一段代码了解一下是aop是怎么运作的

首先定义了两个类实现了MethodInterceptor接口,这里的 MethodInvocation 是对原始方法的包装同时也是AOP通知功能的实现方式。

首先这里method,target,args都是原始对象的方法参数用于反射调用。构造参数中传入集合的是刚刚来两个实现MethodInterceptor的类。

当这里的proceed方法被调用时,会有一个初始化为0的参数,作用是记录这里第几个调用的Advisor,当所有的Advisor调完后就会执行真正的原始方法。从而形成了递归调用,效果就是我们看到的环绕通知

这是主程序加效果

接下来我们看一下Spring如何实现的

实现时要使用@EnableAspectJAutoProxy注解,它的作用是通过封装Import注解导入一些其他类,它导入了 AnnotationAwareAspectJAutoProxyCreator 类

由于此类是被保护的所以我采用继承的方法去调用相关方法,并自定义了一个Advisor,同时还通过注解去引入

复制代码

这是主程序

@Aspect注解的解析

首先这是一个Bean后处理器方法

首先把原始工厂和beanName封装在 MetadataAwareAspectInstanceFactory 类中,此类用于保存@Aspect注解标注的类信息,紧接着调用 this.advisorFactory.getAdvisors 方法

此方法根据类获得方法信息以及注解信息封装为 InstantiationModelAwarePointcutAdvisorImpl 这种类型的Advisor。

Advisor由Advice和切点表达式组成

通过 findEligibleAdvisors 方法获取工厂中所有的Advisor,然后根据bean的类型进行匹配做代理

不过这里值得注意的是导入了一个org.springframework.aop.interceptor.ExposeInvocationInterceptor 这个类,这个类是为了把信息存放在ThreadLocal中暴露给整个线程

顺带说一下这里的wrapIfNecessary就是存放在三级缓存的那个bean代理的创建方法

重点在 wrapIfNecessary 方法,这个方法内部会在 getAdvicesAndAdvisorsForBean 方法中

调用 findEligibleAdvisors 方法

然后调用createProxy方法

重点来了

此方法内部创建一个ProxyFactory对象,他会把传入的Advisor通过装饰器模式把他们转化为实现了MethodInterceptor接口的环绕通知

复制代码
proxyTargetClass 和  exposeProxy 

是@EnableAspectJAutoProxy两个可选项,第一个为false代表默认使用jdk生成动态代理,true的话用Cglib。而exposeProxy为true则代表把生成的代理对象存放在ThreadlocalMap中,这样就可以解决嵌套调用时事务失效的功能。

然后调用getProxy方法生成代理对象

然后我们观察代理对象的代理方法

这里的advised就是ProxyFactory 紧接着调用 getInterceptorsAndDynamicInterceptionAdvice 方法

此方法内部获取所有的Advisor然后进行方法匹配,如果匹配就会把Advice包装为环绕通知

首先获得advice,然后包装

就是这样进行包装,然后通过MethodInvocation获取这些环绕通知,调用invoke这样就可以实现递归嵌套调用,比如前置通知转化为环绕通知后只需要在proceed之前调用就行,就完成了转化

最终调用

就是通过创建 Invocation 然后递归调用

这是官方的proceed实现和我们最开始写的类似

由于上面说了会默认加入一个 ExposeInvocationInterceptor 类用于暴露代理接下来就是证明

这里的invocation就是一个ThreadLocal对象它的作用就是让别的MethodInteceptor也可以使用

就好比通过转化后的 MethodBeforeAdviceInterceptor 就需要用

相关推荐
远望清一色2 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself11 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq041516 分钟前
J2EE平台
java·java-ee
XiaoLeisj23 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man26 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*27 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家28 分钟前
go语言中package详解
开发语言·golang·xcode
llllinuuu29 分钟前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s29 分钟前
Golang--协程和管道
开发语言·后端·golang
王大锤439131 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang