• spring5.0源码解析 从源码角度分析 advice 的执行顺序 aop 05


    从源码角度分析 advice 的执行顺序

    advice 在执行行 不管是 Jdk 的 代理 还是 CGlib 的代理 ReflectiveMethodInvocation 的 proceed()方法去实现的,具体可以去都之前的本专栏的 前几篇 aop 文章,里面有 Aop 实现的整个流程

    配置通知

    @Aspect
    @Component
    public class TestAop {
    
    		@Before("pointCut()")
    	public void before(JoinPoint joinPoint)
    	{
    		Class clz = joinPoint.getTarget().getClass();
    		Signature signature = joinPoint.getSignature();
    		String name = signature.getName();
    		System.out.println("========="+"前置通知:"+name+"===========");
    	}
    	@After("zclvct.spring.aop.TestAop.pointCut()")
    	public void after(JoinPoint joinPoint)
    	{
    		Class clz = joinPoint.getTarget().getClass();
    		String name = clz.getName();
    		System.out.println("========="+"最终通知:"+name+"===========");
    	}
    
    	@Around("zclvct.spring.aop.TestAop.pointCut()")
    	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    		System.out.println("========="+"环绕前"+"===========");
    		joinPoint.proceed();
    		System.out.println("========="+"环绕后"+"===========");
    
    	}
    
    	@AfterThrowing("zclvct.spring.aop.TestAop.pointCut()")
    	public void afterThrowing(JoinPoint joinPoint) throws Throwable {
    		System.out.println("========="+"异常通知"+"===========");
    
    	}
    	@AfterReturning("zclvct.spring.aop.TestAop.pointCut()")
    	public void afterReturning(JoinPoint joinPoint) throws Throwable {
    		System.out.println("========="+"后置通知"+"===========");
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行结果

    =========环绕前===========
    =========前置通知:test===========
    我是userserviceTest01
    =========最终通知===========
    =========后置通知===========
    =========环绕后===========
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从源码角度分析

    在刚进入 proceed 方法时,可以看到整个拦截器链
    在这里插入图片描述

    第一个调用 ExposeInvocationInterceptor

    他维护了 一个 ThreadLocal 对象 用于保存 MethodInvocation 的上下文,在后续的任何下调用链环节,只要需要用到当前的MethodInvocation就通过ExposeInvocationInterceptor.currentInvocation()静态方法获得。

    private static final ThreadLocal<MethodInvocation> invocation =
    			new NamedThreadLocal<>("Current AOP method invocation");
    
    • 1
    • 2
    @Override
    	@Nullable
    	public Object invoke(MethodInvocation mi) throws Throwable {
    	// 设置 线程 MethodInvocation 副本
    		MethodInvocation oldInvocation = invocation.get();
    		invocation.set(mi);
    		try {
    			//递归执行 proceed 方法
    			return mi.proceed();
    		}
    		finally {
    			invocation.set(oldInvocation);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    第二个调用 AspectJAroundAdvice

    第二个调用 AspectJAroundAdvice 的 invoke 方法 invoke 中调用 invokeAdviceMethodWithGivenArgs方法

    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    		Object[] actualArgs = args;
    		if (this.aspectJAdviceMethod.getParameterCount() == 0) {
    			actualArgs = null;
    		}
    		try {
    			ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
    			//反射调用通知方法
    			//this.aspectInstanceFactory.getAspectInstance()获取的是切面的实例
    			return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    		}
    		catch (IllegalArgumentException ex) {
    			throw new AopInvocationException("Mismatch on arguments to advice method [" +
    					this.aspectJAdviceMethod + "]; pointcut expression [" +
    					this.pointcut.getPointcutExpression() + "]", ex);
    		}
    		catch (InvocationTargetException ex) {
    			throw ex.getTargetException();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    其实这个对象通过反射调用的方法其实是 我们之前调用的 TestAop 的 around 方法
    ProceedingJoinPoint

    @Around("zclvct.spring.aop.TestAop.pointCut()")
    	public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    		System.out.println("========="+"环绕前"+"===========");
    		joinPoint.proceed();
    		System.out.println("========="+"环绕后"+"===========");
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此时 参数中的 joinPoint 是一个 MethodInvocationProceedingJoinPoint 实例
    在这里插入图片描述
    在 MethodInvocationProceedingJoinPoint 的 proceed 方法中可以看到
    还是调用的 this.methodInvocation.invocableClone().proceed(); 方法继续递归

    public Object proceed() throws Throwable {
    		return this.methodInvocation.invocableClone().proceed();
    	}
    
    • 1
    • 2
    • 3

    当然 此时 around 方法还没执行完成 只打印
    =========环绕前===========

    第三个调用 MethodBeforeAdviceInterceptor

    这里很简单 调用 在递归前 before 方法 回调 继续递归执行

    public Object invoke(MethodInvocation mi) throws Throwable {
    		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    		return mi.proceed();
    	}
    
    • 1
    • 2
    • 3
    • 4

    第四个调用 MethodBeforeAdviceInterceptor

    这里很简单 调用 在递归前 before 方法 回调 继续递归执行

    public Object invoke(MethodInvocation mi) throws Throwable {
    		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    		return mi.proceed();
    	}
    
    • 1
    • 2
    • 3
    • 4

    第五个调用 AspectJAfterAdvice

    先递归调用 之后 finally 执行 最终通知

    try {
    			return mi.proceed();
    		}
    		finally {
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第六个调用 AfterReturningAdviceInterceptor

    先递归 再调用

    public Object invoke(MethodInvocation mi) throws Throwable {
    		Object retVal = mi.proceed();
    		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    		return retVal;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第六个调用 AspectJAfterThrowingAdvice

    这里 利用 catch 捕获到 异常 在发生异常时执行 同时 将异常 抛出,防止异常被吃掉

    try {
    			return mi.proceed();
    		}
    		catch (Throwable ex) {
    			if (shouldInvokeOnThrowing(ex)) {
    				invokeAdviceMethod(getJoinPointMatch(), null, ex);
    			}
    			throw ex;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    最后调用目标方法

    // 我们从指数-1开始,并提前递增。 递归调用 所有拦截器链中的 Matcher 的 advice
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			// 运行目标对象的方法  也就是反射调用的指定目标
    			return invokeJoinpoint();
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    方法一次返回

    -* AspectJAfterThrowingAdvice 检查异常

    • AfterReturningAdviceInterceptor 如果发生异常不执行
    • AspectJAfterAdvice 最终通知 finally 中运行 一定会执行
    • MethodBeforeAdviceInterceptor 第一次调用已经执行完
    • AspectJAroundAdvice 环绕通知 发生异常不执行,没有异常继续执行
    • ExposeInvocationInterceptor 重新设置 threadLocal变量
  • 相关阅读:
    windows 安裝字體Font
    Verilog:【7】超详细WaveDrom教程,时序图绘制利器,看这一篇就够了。
    浮动元素的特点(2)
    linux驱动开发led绑定亮灯
    Google关键词挖掘方法
    基于PHP的幸福街道的宠物管理平台的设计与开发
    cadence SPB17.4 - allegro - Allegro2Altium.bat 初探
    SSO系统设计框架搭建
    08 数据库查询(2) | OushuDB 数据库使用入门
    MySQL——增删改查
  • 原文地址:https://blog.csdn.net/qq_44808472/article/details/126292785