AOP核心概念
1、横切关注点(对哪些方法进行切入)
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect, 巴原来糅杂在业务逻辑代码中的非业务代码抽取出来, 把功能相同的放在一个类中形成一个切面)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(join point)(需要切入的点)
被拦截到的点, 因为Spring只支持方法类型的连接点, 所以在Spring中连接点指的就是被拦截到的方法, 实际上连接点还可以是字段或者构
造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
把通知和业务代码混合在一起执行,这个过程叫做积入。
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
官网看资料
接下来学习的就是通过全注解的模式情况下进行分析
项目实践环境
如果要为每一个参数做校验,做校验的逻辑都是相同的,
那么把相同功能的非业务代码放到一个类里面去,这个动作叫做抽取横切关注点形成切面,
那么被切的方法是在执行方法之前,还是之后切进去呢?这个入口如何确定?
先准备基础环境,当基本环境已经OK了。先思考:
1、为什么学习源码?
为了学习源码中的设计思想,设计思路,然后将其应用到业务代码中
线下如果出现bug,能通过源码快速定位
2、怎么学习源码?
3、比如一些校验,一般是如何做?
第一是:可以ifelse (很low)
第二是:可以动态代理进行切入
第三是:可以通过aop,面向切面进行切入,
(如果要符合开闭原则)将所有的校验规则设置成校验类,实现校验接口,通过触发选择调用的规则即可
接下来将会完成以下几点
1、学习高效的阅读源码技巧和方法
2、Spring Aop切面核心源码剖析
3、Bean的生命周期BeanPostProcessor工作原理剖析(Spring容器初始化的前后调用后置处理器)
项目构建完毕后如何如何学习源码
1、先打断点看返回什么东西
2、发现返回的是代理对象。
那么就有如下疑问?
代理对象存放在那里了? 代理对象是什么时候生成的? 那调用的目标对象calculate.div()去哪里了
带着第一个问题, 代理对象存放在那里了
3、带着这三个问题看源码
因为上面已经返回了代理对象,那么我就看你做的是什么事情!
重新打断点,debug进去
注意:目前是想知道是为什么返回代理对象,所以下面读的源码也是围绕着能返回代理对象的代码看。
如果是返回其他的,则先忽略不看。
这个有需要返回哦
进来是找代理对象的,所以看那句代码是返回代理对象的。其他的先不看。
具体要不要看呢?可以通过执行完之后的结果来判断,需不需要去看。
一路走完后发现,单例缓存池有返回代理对象,那么就重新debug进去。
注意:把每个要看的代码给记住,就是所闻的主干流程。
流程一路走下来,看返回结果,然后发现,代理对象是从单例缓存池中获取的。
第一次看的时候要注意:出现这种 map.get 的形式,那肯定有 map.put 因为存取守恒。
所以接下来是全局搜索 map.put。然后打断点,重新debug,看它怎么放入的。
解决第一个问题,代理对象是哪里取出来的问题。
带着第二个问题, 代理对象是什么时候生成的
为了快速定位,所以在放入单例缓存池这行代码打断点,并添加过滤条件
这个可以通过调用栈进行反推判断
这两个截图是反推的细节
反推到这行调用代码,发现返回的是空的,而刚刚上面的那个返回的还有代理对象。
说明就是上面那行代码得到的代理对象。
需要在这行代码打断点,然后看里面是如何的逻辑。
打完断点,重新debug进来,当代码停在断点处,就继续进入,看里面的细节
进来后干啥呢?进来是要找代理对象怎么产生的?所以带着这个目的,先走完流程,看那行代码返回代理对象
一路走下来发现,它从工厂里面取出了(三级缓存取出了),
那么又是什么时候放入三级缓存的呢?
点进去看,发现直接从三级缓存中获取了。
但是注意哦,这个三级缓存是随着参数的传入而传入的,并不是在这个方法里面调用其他的产生的哦
所以得回头看一下这个传入的三级缓存。
退一步发现,是一个lambda表达式出入上面的三级缓存,那么lambda表达式里面调用了createBean方法,那么打断点,继续看这个createBean是如何产生的代理对象。
=====================》》》》
关于这里要具体剖析一下lambda表达式::
lambda分析1
这是一整个lambda表达式,它传入了getSingleton方法里面,那么就点进这个方法。
lambda分析2
lambda分析3
lambda分析4
=====================》》》》
方式一:一路走下来看那行代码返回代理对象,再重新debug进去看
方式二:Spring代码中,doXXX都是干活的方法。
一路走下去,看那行代码返回代理对象
上面通过调用无参构造,生成的目标对象。
目标对象有了,怎么转为代理对象呢?
一路走下来,发现放入了二级缓存了,也就是那个工厂里面。
将目标对象从三级放入到二级缓存中
代码一路走下来后,发现返回了代理对象的代码行。
重新debug进去看。
它能返回代理对象,那怎么产生的呢?
先执行流程,看那行返回代理对象,就进入那行代码看。
一路走下来发现,在调用后置处理器的时候,返回代理代理对象。
那么代理对象就是后置处理器的方法产生的,重新debug。进去看
到这里有太多的后置处理器了。怎么找,无头绪是不是,
方式一:一个个遍历,找到代理相关的类执行
方式二:去通过注解来揭开这个疑问!
从AOP注解入手开始
到这里就发现,EnableAspectJAutoProxy这个注解,
为容器添加了一个组件AnnotationAwareAspectJAutoProxyCreator(自动代理创建器)
就是前面的这个注解,添加了后面的这个组件。
换句话说,添加了@EnableAspectJAutoProxy这个注解,就相当于配置了一个AnnotationAwareAspectJAutoProxyCreator类了。(也可以理解为配置了一个Bean)
这里采用了模板设计模式
进到对应的类,按Ctrl+Alt+Shift +u 看结构图
到此,清晰的解析了aop代理对象的产生,目标对象的去处了。
开始解答问题
aop默认采用什么代理模式
如果不指定(默认属性为false),就先看业务类有没有实现接口,有接口就是jdk,没有接口就是cglib
如果指定为true,就强制使用cglib
第3点:
两个
看业务代码
第4、5点:事物的切面和自定义的切面的优先级
方式一:实现Order接口
方式二:添加了Order注解后(默认是值越大,优先级越低)
如果优先级是一样的,那么就和解析顺序相关,先解析事物的,在解析自定义的
如果要想修改优先级,就越高的值就越小
如果想自定义的切面优先级要高于事物的,就定义一个最小值(优先级最高)
第6点:aop如何解析切面
通过之前代码分析,晓得切面是在createBean哪一步就有了。接下来debug源码看解析切面:
为什么要放入缓存呢,因为ioc容器有很多对象的时候,就需要一个个遍历。就是否的耗损性能。
============如何解析切面的=================
AbstractAutowireCapableBeanFactory#createBean
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
Aop代理对象是如何织入调用核心源码的
(就是如何调用目标方法的)
132页开始看(重点是如何织入的)
已经晓得返回的是代理对象,那么代理对象是怎么调用目标方法进行织入的呢
所有的自定义切面信息都被存到代理对象里面,其实就是为了方便织入
那么是如何调用目标方法的呢?
第一次
注意: 上面传入的是MethodInvocation,为什么呢?因为它里面包含有链的顺序,接下来调用就得按这个顺序去地哦啊用的。不然他怎么晓得调用那个呢?(必须知道下一个对象是哪个)
为什么每次都要回调呢,通过proceed()方法进行递归回调,是因为要知道下一个执行的是哪一个对象
因为所有对象的顺序都存在了Chain中。只有通过回调,下标++来获取下一个要执行的目标。
第二次
第三个
第四个
第五个
进行入一次,AopUtils类中,然后返回,再进入。
然后就是一路返回了。
只是把前置和后置的方法罗列清楚,当执行完毕进行返回,只是为了维护执行逻辑和执行顺序。
1、当所有都执行完后,先调用after,如果有异常,就先调用异常。
2、
通过责任链+递归来控制执行的顺序。以及各个分支的支路执行,可以将其精髓应用到业务代码中
通过节点驱动执行,第一个驱动第二个,第二个读取驱动第三个,第三个驱动第四个。执行到最后一个适时候就会执行目标方法。执行完目标方法就一路返回。
其实是对五个链的顺序进行排序,其保存顺序就是执行顺序。
如何将此理念应用到业务代码中
写代码时候有各种校验
第一是:可以ifelse (很low)
第二是:可以动态代理进行切入
第三是:可以通过aop,面向切面进行切入,
(如果要符合开闭原则)将所有的校验规则设置成校验类,将其共性的方法抽取出来,形成抽象校验类,然后抽象校验类来实现校验接口,然后校验的规则类来继承抽象类,通过触发选择调用的规则即可(模板模式)
如何能设置成灵活的配置呢。
可以通过依赖查找!(可能听过依赖注入 autowrite只能注入一个)
而通过依赖查找呢,就能将容器中所有实现类的组件都注入到map中,这就是依赖查找
可以挑选出对应的规则类进行校验查找。
然后引入一个类似于ReflectiveMethodInvocation的驱动校验规则类去执行。
依赖查找+递归+责任链+模板(总比很多的if else 好吧)
灵活+符合开闭原则+配置简单,这就是设计模式的重要性
aop 做分布式事物、分库分表、TCC、
这个链是通过排序的!
1、全局搜索sortAdvisors方法
实现了拓扑排序,则每次的排序结果可能不一样(根据配置的顺序有关)
对于事物如何拦截的
对事物如何应用切面
全局搜索TransactionInterceptor
Spring AOP源码分析全集2019年B站最新出品_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
Spring技术自定义注解和Aop实现事务框架,阿里P7都要掌握的技术~_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
听说你很懂源码?Spring读懂了?这些IOC,AOP,Spring bean,循环依赖等等问题接得住吗?_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
[
](https://www.bilibili.com/video/BV1rK4y1h7LM?from=search&seid=15077766374279074205)