Spring 5.x 源码之旅-49AOP通知方法执行顺序探究

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

AOP通知方法如何执行

先看下整体的AOP基本通知方法的内部执行顺序结构:


我们拿JDK动态代理为例,CGLIB也是类似,当调用某个方法的时候,会进行JdkDynamicAopProxy代理的invoke执行:

    @Override
    	@Nullable
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object oldProxy = null;
    		boolean setProxyContext = false;
    
    		TargetSource targetSource = this.advised.targetSource;
    		Object target = null;
    
    		try {
    			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    				// The target does not implement the equals(Object) method itself.
    				return equals(args[0]);
    			}
    			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
    				// The target does not implement the hashCode() method itself.
    				return hashCode();
    			}
    			else if (method.getDeclaringClass() == DecoratingProxy.class) {
    				// There is only getDecoratedClass() declared -> dispatch to proxy config.
    				return AopProxyUtils.ultimateTargetClass(this.advised);
    			}
    			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
    					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    				// Service invocations on ProxyConfig with the proxy config...
    				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    			}
    
    			Object retVal;
    
    			if (this.advised.exposeProxy) {
    				// Make invocation available if necessary.
    				oldProxy = AopContext.setCurrentProxy(proxy);
    				setProxyContext = true;
    			}
    
    			// Get as late as possible to minimize the time we "own" the target,
    			// in case it comes from a pool.
    			target = targetSource.getTarget();
    			Class<?> targetClass = (target != null ? target.getClass() : null);
    
    			// Get the interception chain for this method.
    			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
    			
    			MethodInvocation.
    			if (chain.isEmpty()) {
    				// We can skip creating a MethodInvocation: just invoke the target directly
    				// Note that the final invoker must be an InvokerInterceptor so we know it does
    				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
    				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    			}
    			else {
    				// We need to create a method invocation...
    				MethodInvocation invocation =
    						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    				// Proceed to the joinpoint through the interceptor chain.
    				retVal = invocation.proceed();
    			}
    
    			// Massage return value if necessary.
    			Class<?> returnType = method.getReturnType();
    			if (retVal != null && retVal == target &&
    					returnType != Object.class && returnType.isInstance(proxy) &&
    					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
    				// Special case: it returned "this" and the return type of the method
    				// is type-compatible. Note that we can't help if the target sets
    				// a reference to itself in another returned object.
    				retVal = proxy;
    			}
    			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
    				throw new AopInvocationException(
    						"Null return value from advice does not match primitive return type for: " + method);
    			}
    			return retVal;
    		}
    		finally {
    			if (target != null && !targetSource.isStatic()) {
    				// Must have come from TargetSource.
    				targetSource.releaseTarget(target);
    			}
    			if (setProxyContext) {
    				// Restore old proxy.
    				AopContext.setCurrentProxy(oldProxy);
    			}
    		}
    	}

一堆,其实重要的就这么几句,首先获取所有通知List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);然后封装成ReflectiveMethodInvocation调用proceed方法。我把AOP的一般方法都用上了,可以看到这里他已经是排序完的,具体怎么排序的,可以去研究下,比较复杂,其实就是为了形成一个调用栈顺序,后面我会画出来,我们先看这些用到的,第一个其实没什么具体用,他会直接执行第二个,然后顺序执行下去:

ReflectiveMethodInvocation的proceed循环执行

主要是递归获取通知,然后执行。

    @Override
    	@Nullable
    	public Object proceed() throws Throwable {
    		// 循环执行,直到执行完
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			return invokeJoinpoint();
    		}
    		//获得通知方法
    		Object interceptorOrInterceptionAdvice =
    				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			// Evaluate dynamic method matcher here: static part will already have
    			// been evaluated and found to match.
    			InterceptorAndDynamicMethodMatcher dm =
    					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    				return dm.interceptor.invoke(this);
    			}
    			else {
    				// Dynamic matching failed.
    				// Skip this interceptor and invoke the next in the chain.
    				return proceed();
    			}
    		}
    		else {//执行
    			// It's an interceptor, so we just invoke it: The pointcut will have
    			// been evaluated statically before this object was constructed.
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}

ExposeInvocationInterceptor的invoke

没做什么,主要是mi.proceed(),也就是继续调用ReflectiveMethodInvocation的proceed来进行递归调用。

    @Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		MethodInvocation oldInvocation = invocation.get();
    		invocation.set(mi);
    		try {
    			return mi.proceed();
    		}
    		finally {
    			invocation.set(oldInvocation);
    		}
    	}

AspectJAfterThrowingAdvice的invoke异常通知

其实就是在外面包了一层捕获异常,然后继续调用mi.proceed();,如果出异常了可以捕获到执行invokeAdviceMethod

    	@Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		catch (Throwable ex) {
    			if (shouldInvokeOnThrowing(ex)) {
    				invokeAdviceMethod(getJoinPointMatch(), null, ex);
    			}
    			throw ex;
    		}
    	}

AbstractAspectJAdvice的invokeAdviceMethod通知方法

其实这个是通用的通知方法,只要有要调用的通知方法,都会去调用这个内部最终调用了这个:

AfterReturningAdviceInterceptor的invoke成功返回后通知

只是在返回后加了方法,内部就是调用invokeAdviceMethod

    	@Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		Object retVal = mi.proceed();
    		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    		return retVal;
    	}

AspectJAfterAdvice的invoke方法后通知

就是在执行完后执行invokeAdviceMethod

    	@Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		finally {
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    	}

AspectJAroundAdvice的invoke环绕通知

这个会先把MethodInvocation封装成ProceedingJoinPoint,然后在调用的时候当做参数传进去。

    	@Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		if (!(mi instanceof ProxyMethodInvocation)) {
    			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    		}
    		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    		ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    		JoinPointMatch jpm = getJoinPointMatch(pmi);
    		return invokeAdviceMethod(pjp, jpm, null, null);
    	}

先封装:


后使用:


然后里面会进行克隆,或许是代理为了保护源数据信息吧,然后再调用proceed

MethodBeforeAdviceInterceptor的invoke方法前通知

先调用方法前通知方法,在调用方法。

    	@Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    		return mi.proceed();
    	}

基本的流程就是这样下来的,所以为什么环绕是在最前面,因为环绕代理了后面要执行的方法,提前进行了环绕前处理:


如果有异常的话:


再看下整个结构是这样的:

HM6Ly9ibG9nLmNzZG4ubmV0L3dhbmd3ZWkxOTg3MTEwMw==,size_16,color_FFFFFF,t_70)
正常情况可以对照,就是没有异常的,没啥问题。
如果出现异常,就是出现在System.out.println("方法调用");因此直接执行System.out.println("logAfter");,不会去执行logReturning,直接进到catch里面了,跟异常时候的信息也对的起来。

好了,基本的AOP调用分析完了,其实就是通知方法排序好之后,递归调用。其实还有其他的功能,比如DeclareParentstargetargs等注解功能,这个基础的有了,应该不难,自己研究下好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值