Spring基础系列-AOP织入逻辑跟踪


原创文章,转载请标注出处:《Spring基础系列-AOP织入逻辑跟踪》


其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置、后置、环绕、异常等通知是如何围绕在目标方法周围执行的呢?

这里面最重要的就是递归,Spring在实现这块逻辑的时候使用的大量的递归调用,完美的实现的织入的逻辑。

我们不凡就以Spring基础系列--AOP实践中的例子来进行一番逻辑追踪,来一探究竟。

我们就从测试类开始:


首先我们通过CglibAopProxy类的intercept方法构建一个方法调用,并执行其proceed方法:

然后进入ReflectiveMethodInvocation类的proceed()方法,由于currentInterceptorIndex下标从-1开始,在进行++自增操作之后变成0,获取interceptorsAndDynamicMethodMatchers中的第一个拦截器。

我们看下interceptorsAndDynamicMethodMatchers中的内容:

第一个是ExposeInvocationInterceptor,这个正是之前在Spring基础系列-AOP源码分析中源码11里提到的,获取通知链的时候会将其放到通知链首位,用于暴露方法调用到ThreadLocal中。

由于其为一个简单的拦截器,上面的判断是不成立的,所以直接执行最后一行:

然后进入到ExposeInvocationInterceptor方法的invoke方法中:

在这个方法中我们可以看到,90行处,将方法调用mi放到了invocation中。那invocation是什么呢?如下:

private static final ThreadLocal<MethodInvocation> invocation = new NamedThreadLocal<>("Current AOP method invocation");

可见,是将方法调用保存到了ThreadLocal中,这个invocation保存的正是当前AOP的方法调用。

然后执行mi.proceed()方法,这就是一个递归调用了。在这里执行proceed方法,将会继续执行下一个通知,执行逻辑又回到ReflectiveMethodInvocation类的proceed()方法,这时currentInterceptorIndex自增之后为1,正好指向interceptorsAndDynamicMethodMatchers中的下一个通知:

很显然,下一个通知为后置异常通知afterThrowingTest。

执行这个通知的invoke方法,代码调到AspectJAfterThrowingAdvice类的invoke方法。

在异常通知的invoke方法中直接递归调用proceed方法,并将剩下所有的调用执行全部try...catch住,在最后catch块中执行其通知逻辑。

除开普通的拦截器之外,最先执行的通知是异常通知,它会将剩下所有的调用逻辑全部catch住,也就是说在这期间发生的任何异常都会被捕捉到,而且一旦哪一步发生了异常,那么执行就会被中断,到这里执行catch逻辑。

这下又回到了ReflectiveMethodInvocation类的 proceed方法。currentInterceptorIndex再次自增为2,指向通知链中第三个通知:

后置返回通知,遵从以上的逻辑我们看看下一步的行为:

AfterReturningAdviceInterceptor类中

果然去执行后置返回通知的invoke方法去啦,第一步任然是递归调用,又回到了ReflectiveMethodInvocation类的proceed方法去执行第四个通知:

这个是后置终点通知,这个通知有点特殊哦,我们来看看其invoke方法

它也是先直接进行递归调用,将其逻辑放在最后,这个是真正的最后,直接放到了finally块中,这表示什么,这表示,无论接下来的调用操作是否会发生异常,这部分逻辑永远会执行。

现在又一次回去了ReflectiveMethodInvocation类的proceed方法去执行第五个通知:

嗯,这次是环绕通知,这个通知不同于其他通知,其他通知只执行于方法的一点,这个通知却需要执行于方法的两处。所以它会拥有一个ProceedJoinPoint参数,这个参数就是用来区分这两处执行逻辑的:目标方法之前与之后。

直接执行到最后一句,首次要执行invokeAdviceMethod方法了:

然后执行invokeAdviceMethodWithGivenArgs方法:

它要直接执行通知方法中的逻辑,想想为什么没有在进行递归调用而是直接执行的通知方法呢?

因为环绕通知将递归调用挪移到了通知逻辑中,由程序员自定义执行,来看看我自定义的环绕通知逻辑:

我们先不考虑具体的逻辑,先看看大体结构,显示一段逻辑,然后执行jp.proceed(os)方法。然后又是一段逻辑。所谓的环绕就是将逻辑围绕在目标执行前后。

通知的内容真正开始执行了,这里首先执行了环绕通知的前置部分。

我们执行递归调用之后,我们发现逻辑跑到了MethodInvocationProceedingJoinPoint中,这是啥类呢?

这竟然是ProceedJoinPoint的实现类,我们执行jp.proceed(os)方法当然会跑到这里了。

其实ProceedJoinPoint的proceed方法有两个重载,一个有参数,一个无参数,分别用于针对目标方法有参数和目标发无参数的情况,所以这里这两个方法其实逻辑类似,只是有参数的方法会针对参数进行一番操作,将参数设置到方法调用中,这么做,新的方法参数会替换就的方法参数,这也是我们可以在环绕通知中修改参数的原理所在。

第99行就是重设参数的逻辑,方法调用中原先其实已经保存有参数:原有的参数。

第100行代码是重点,这里这里创建了一个方法调用的一个浅拷贝,并使用这个浅拷贝来执行递归调用proceed方法。

为什么使用浅拷贝呢?因为我们还希望使用与原来的方法调用相同的拦截器链和其他对象引用,只不过是需要当前的环绕通知的独立性罢了(这个可能就是再之前提到的环绕通知会导致其他一些通知功能失效的原因所在了吧)

逻辑又回到ReflectiveMethodInvocation类的proceed方法去执行第六个通知:

这最后一个通知必然就是剩余的前置通知了。让我们看看其invoke方法执行。来自:MethodBeforeAdviceInterceptor

这里执行通知的前置通知逻辑:来自:AspectJMethodBeforeAdvice

调用跑到AbstractAspectJAdvice中:

再调用:

这里就是真正调用通知逻辑了:来自AspectTest

然后就是一路后退到MethodBeforeAdviceInterceptor类中的invoke方法,继续执行下一步:

这又是一递归调用,这个调用将会使逻辑再次返回到ReflectiveMethodInvocation类的proceed方法,这一次,由于 所有的通知链中的通知都走过一次,剩余的就是目标方法了。来自:CglibAopProxy

所以这次直接执行目标方法了,哈哈。来自:AspectDemo

反射调用目标方法。

执行完目标方法,然后递归退回到AspectTest类中的环绕通知aroundTest中去执行环绕通知的后置部分逻辑。

执行完后,继续递归退回,到AspectJAfterAdvice类的invoke方法,去执行finally中的逻辑:

再次执行AbstractAspectJAdvice类的invokeAdviceMethod方法:

调用invokeAdviceMethodWithGivenArgs方法

这次执行后置终点通知中的内容:

然后再次递归回退到AfterReturningAdviceInterceptor类的invoke方法:

调用AspectJAfterReturningAdvice类的afterReturning方法:

这里不再罗列AbstractAspectJAdvice类中的那两个方法啦,直接出通知方法:

这次退回到AspectJAfterThrowingAdvice类的invoke方法:

由于整套逻辑未发生异常,所以此处不执行catch块中的逻辑。然后再次递归回退到了ExposeInvocationInterceptor类的invoke方法,来执行finally块中的逻辑:

这句话将invocation中保存的方法调用置空了。最后回退到DynamicAdvisedInterceptor类的intercept方法继续下面的逻辑。

好了,到此为止,想说的都说啦,下面就是一些补充:

这里是为了统一说明,所以讲所有的通知全部罗列,在一起调用,很明显返回值的部分就不正确,我想说的是,尽量别把环绕通知和别的通知一起使用,这个通知还是单独使用比较好。

怎么样?如果你全都看完了,那么有啥感想吗?

织入的实现就是依靠一个顺序通知链,再加上递归实现的。有没有感觉这里的递归用的非常的漂亮。层层嵌套,将后执行的部分放到通知链前面,在递归链中以先执行下一条通知的方式层层深入,最里面是首先执行的通知(前置通知,先把环绕通知撇开),然后再层层退出,执行各自通知的逻辑包括目标方法的逻辑。

之前我有个疑惑就是在ReflectiveMethodInvocation类的proceed方法中,前三行代码,顺序执行通知链,最后执行目标方法,那么怎么实现后置的通知呢。看完上面的代码追踪,就了解了吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354