【Spring源码】25.AOP之代理执行时切面的链式调用

上一篇讲了,aop代理执行时,所有匹配该方法的切面都已经收集完毕,接下来就是 以责任链的模式 去遍历切面集合,来挨个增强原生方法。

image.png

5. 代理执行链的调用

如果没找到匹配该方法的切面集合,那么就返回调用原方法

image.png

找到了,就匹配该方法的切面集合,就进行切面执行链的调用

会先创建一个代理执行上下文对象MethodInvocation,把所有的切面,代理对象,原生方法,参数,等等传进去

image.png

然后调用MethodInvocation对象的proceed()方法,jdk动态代理实现用的是ReflectiveMethodInvocation类

image.png

测试代码

为了更直观的 描述切面执行链的调用过程,这里准备点测试代码,把五种aop方法代理类型的注解都加上

被代理的类和被代理的方法

@Component
public class MyBeanServiceImpl implements MyBeanService {
    @Override
    public String testMyBean() {
        System.out.println("原生方法被执行...");
        return "MyBeanServiceImpl.testMyBean方法的返回值";
    }
}

aop配置类

aop配置类,五种aop代理类型的类型都加上

@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* com.lb.springboot_simple_starter.bean.aop.service.impl.MyBeanServiceImpl.*(..))")
    public void pc2() {}

    @Before(" pc2()")
    public void before() {
        System.out.println("前置通知");
    }

    @After(" pc2()")
    public void after() {
        System.out.println("后置通知");
    }

    @Around("pc2()")
    public void around(ProceedingJoinPoint point) {
        System.out.println("环绕通知,原方法执行前");
        try {
            System.out.println("环绕通知中:" + point.proceed());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕通知,原方法执行后");
    }

    @AfterThrowing(value = " pc2()", throwing = "e")
    public void AfterThrowing(Exception e) {
        System.out.println("异常通知");
        MethodInvocation methodInvocation = ExposeInvocationInterceptor.currentInvocation();
        e.printStackTrace();
    }

    @AfterReturning(value = "pc2()", returning = "result")
    public void doAfterReturning(Object result) {
        System.out.println("【返回后通知】 返回值:" + result);
    }
}

运行;

从spring容器中获取MyBeanService代理对象,并调用testMyBean()。

@Test
public void testGeneratorAdvisor(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.lb.springboot_simple_starter.bean.aop");
    MyBeanService bean = applicationContext.getBean(MyBeanService.class);
    bean.testMyBean();
    System.out.println(bean);
    //MyService bean = applicationContext.getBean(MyService.class);
    //bean.getUser();
}
image.png

ReflectiveMethodInvocation.proceed().

接下来,所有切面与当前执行方法匹配后,进入ReflectiveMethodInvocation.proceed()

image.png

可以看到有有一个默认的切面,加我们自己配置的五个切面:按照Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 注解所对应的切面类 排序。

然后这里有个currentInterceptorIndex变量,默认为-1, 来控制 从数组取出切面的下标。++后从数组取出切面对象。++后为0。

image.png

1.执行ExposeInvocationInterceptor

currentInterceptorIndex 初始为0,取出数组0下标对应的切面类 ExposeInvocationInterceptor对象

image.png

这里千万要注意,在调用 实现MethodInterceptor接口的所有切面实现类的invoke方法时会传入this,也就是当前MethodInvocation对象

ExposeInvocationInterceptor.invoke()

在 把MethodInvocation对象对象放入到ExposeInvocationInterceptor的 ThreadLocal<MethodInvocation> invocation后

image.png

调用当前MethodInvocation对象.proceed()方法,进行递归,代码又回到了这个

image.png

2.执行AspectJAroundAdvice

上一轮取出ExposeInvocationInterceptor后, currentInterceptorIndex 已经被++成0了,这里++ = 1,取出下边为1的切面对象 : AspectJAroundAdvice

AspectJAroundAdvice用来处理@Around注解的

image.png

AspectJAroundAdvice.invoke

image.png

调用AspectJAroundAdvice类的父类 AbstractAspectJAdvice的invokeAdviceMethod方法

这里注意下父类 AbstractAspectJAdvice的invokeAdviceMethod方法 是专门用来执行 增强逻辑的方法,也就是有@Around,@Before,@After,@AfterReturning,@AfterThrowing 的方法。切面对象 是会存有 对应方法的Method对象

image.png

然后反射调用 有@Around注解的方法

image.png

这样就会被调用到我们自己写的@Around方法。

image.png

@Around注解下,我们一般会把增强的逻辑代码写在point.proceed()前后

point.proceed()方法 就又会递归到MethodInvocation的procceed()方法,把切面执行链传递下去。

千万注意,此时这个方法 还未出栈,也就是 point.proceed()方法后的方法 还要等 point.proceed()方法执行完后再执行。

image.png

接下来代码又会回到MethodInvocation的procceed()。

image.png

3.执行MethodBeforeAdviceInterceptor

上一轮取出AspectJAroundAdvice后, currentInterceptorIndex 已经被++成1了,这里++ = 2,取出下边为2的切面对象 : MethodBeforeAdviceInterceptor

MethodBeforeAdviceInterceptor用来处理@Before注解的

image.png

MethodBeforeAdviceInterceptor.invoke

这里处理@Before注解的AspectJMethodBeforeAdvice类 是经过适配的,由MethodBeforeAdviceInterceptor持有它,

MethodBeforeAdviceInterceptor.invoke会 先调用AspectJMethodBeforeAdvice的before(),去调用@Before的方法

image.png
image.png

这里由会调用到公用的 父类AbstractAspectJAdvice去执行advise对象里面 的增强方法,返回调用@Before方法

image.png

MyAspect.before()调用

image.png

接下来该方法结束,

代码回到 MethodBeforeAdviceInterceptor.invoke方法,又调用MethodInvocation得proceed(),把执行链传递下去

image.png

代码回到这里MethodInvocation.proceed()

image.png

4.执行 AspectJAfterAdvice

上一轮取出AspectJAroundAdvice后, currentInterceptorIndex 已经被++成2了,这里++ = 3,取出下边为3的切面对象 : AspectJAfterAdvice

AspectJAfterAdvice用来处理@After注解的

image.png

AspectJAfterAdvice.invoke(mi)

@After切面的处理逻辑就有点奇特了,他先调用MethodInvocation的proceed(),把执行链传递下去,

再在finally代码块去 调用@After方法,也就是会等剩下的 @AfterReturning,@AfterThrowing切面执行完 以及原生方法 执行完,再去调用 调用@After方法

image.png

这里AspectJAfterAdvice.invoke还没出栈,先把执行链传递下去,代码又回到MethodInvocation.proceed()

image.png

5.执行 AfterReturningAdviceInterceptor

上一轮取出AspectJAfterAdvice后, currentInterceptorIndex 已经被++成3了,这里++ = 4,取出下标为4的切面对象 : AfterReturningAdviceInterceptor

AfterReturningAdviceInterceptor用来处理@AfterReturning注解的

image.png

AfterReturningAdviceInterceptor.invoke(mi)

这里处理@AfterReturning注解的AfterReturningAdvice类 是经过适配的,由AfterReturningAdviceInterceptor.invoke持有它,

AfterReturningAdviceInterceptor.invoke可以调用AfterReturningAdvice的afterReturning(),去调用@AfterReturning的方法。

@AfterReturning注解的作用就是 等原生方法返回之后,再执行@AfterReturning方法,所以这里原生方法还没调用,更不会有返回值,所以把执行链传递下去,等剩下的@AfterThrowing注解处理完,原生方法调用完获取到返回值之后,再执行 AfterReturningAdvice的afterReturning(),去调用@AfterReturning的方法。

image.png

这里AfterReturningAdviceInterceptor.invoke(mi)还没出栈,先把执行链传递下去,代码又回到MethodInvocation.proceed()

image.png

6.执行AspectJAfterThrowingAdvice

上一轮取出AfterReturningAdviceInterceptor后, currentInterceptorIndex 已经被++成4了,这里++ = 5,取出下标为5的切面对象 : AfterReturningAdviceInterceptor

AfterReturningAdviceInterceptor用来处理@AfterThrowing注解的

image.png

AspectJAfterThrowingAdvice.invoke(md)

@AfterThrowing注解的作用就是 原生方法执行时抛出异常后,再执行@AfterThrowing方法,并传入异常对象并进行处理增强,所以这里原生方法还没调用,

所以先try_catch, 先调用MethodInvocation的proceed方法,把执行链传递下去,等原生方法调用抛出异常时,catch到异常,再执行 invokeAdviceMethod方法 ,去调用@AfterThrowing的方法。

image.png

所以这里,代码又回到了MethodInvocation的proceed方法,注意此时AspectJAfterThrowingAdvice.invoke方法 并未出栈

image.png

7.调用原生方法

我们一开始就只有6个切面,当上一个切面执行AspectJAfterThrowingAdvice 被取出时,currentInterceptorIndex被++,已经从4变到5了,所以再次进入MethodInvocation的proceed,会判断所以切面是否被执行完

判断 currentInterceptorIndex 是否等于 切面数组长度 -1,等于的话,说明所有切面都已经被调用了。

image.png

所有切面被调用了,那么接下来就只有调用原生方法了,也就是被代理方法

image.png

invokeJoinpoint()反射调用原生方法

就是反射调用 原生方法

image.png

传入被代理方法,反射调用method


image.png

进入我们的原生方法,返回返回值,不抛出异常

image.png

然后方法结束

8.AspectJAfterThrowingAdvice.invoke(md)出栈

之前最后一次进入MethodInvocation.proceed()是在 AspectJAfterThrowingAdvice.invoke(md) 时,方法内部 调用的,所以,这里原生方法被执行return 后,回到 AspectJAfterThrowingAdvice.invoke(md)

这里没抛出异常,所以不会走catch里面的逻辑 : 判断抛出异常是否等于 @AfterThrowing里配置的异常,是的话,就调用@AfterThrowing方法,传入异常对象

image.png

这里return出去后,ReflectiveMethodInvocation.proceed 也return

image.png

9.AfterReturningAdviceInterceptor.invoke(md)出栈

调用AspectJAfterThrowingAdvice.invoke的MethodInvocation.proceed ()是由AfterReturningAdviceInterceptor.invoke(mi)调用的,所以MethodInvocation.proceed () 返回后,代码回到 AspectJAfterThrowingAdvice.invoke(md)

image.png

接下来调用AfterReturningAdvice的afterReturning方法,去调用@AfterReturning方法

image.png

invokeAdviceMethod肯定又是公用父类AbstractAspectJAdvice里面的,反射调用AfterReturningAdvice对象里存储的@@AfterReturning方法 的Method对象,并传入返回值

image.png
image.png

然后@AfterReturning方法返回

AfterReturningAdviceInterceptor.invoke返回

image.png

ReflectiveMethodInvocation.invoke方法返回

image.png

10. AspectJAfterAdvice.invoke出栈

代码回到上一个调用 MethodInvocation.invoke()方法的AspectJAfterAdvice.invoke()方法内

image.png

然后在finally块调用invokeAdviceMethod() ,invokeAdviceMethod()肯定又是公用父类AbstractAspectJAdvice里面的,去调用@After方法的Method对象,这里就不赘述了,直接进入到@After方法

这里和@AfterReturning方法的区别就是不在乎返回值

image.png

然后AspectJAfterAdvice.invoke出栈,调用它的 MethodInvocation.invoke()也随之出栈

image.png
image.png

12.MethodBeforeAdviceInterceptor.invoke(md)出栈

代码回到上一个 调用 MethodInvocation.proceed()的 MethodBeforeAdviceInterceptor.invoke(md)里

image.png

这里MethodInvocation.proceed(),MethodBeforeAdviceInterceptor.invoke(md)也return,又回到 MethodInvocation.proceed() 去return

image.png

13.@Around方法出栈

上一次调用MethodInvocation.proceed() 是在我们@Around方法的内部,手动调用的,所以MethodInvocation.proceed() return后 代码又回到这,

image.png

接着执行point.proceed() 后面的代码,结束@Around方法

然后代码回到 调用@Around方法的AspectJAroundAdvice.invoke方法

image.png

MethodInvocation.proceed() return

image.png

14.ExposeInvocationInterceptor出栈

代码回到最初 调用MethodInvocation.proceed()的 ExposeInvocationInterceptor.invoke中

直接return 执行完 MethodInvocation.proceed() 后执行finally, 把旧的 aop代理执行上下文 设置回去 ,方法结束,ExposeInvocationInterceptor.inoke方法也随之出栈结束了。

image.png

15.JdkDynamicAopProxy.invoke 出栈

至此,所有的aop切面执行完了, JdkDynamicAopProxy 这个InvokeHandler的 invoke方法执行完,返回返回值,代理对象的代理逻辑也就彻底走完了。

image.png

代理对象的方法 调用就彻底完成了

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

推荐阅读更多精彩内容