Spring AOP源码学习:一次请求调用全流程(2)

这篇文章详细描述了SpringAOP中MethodInvocation的处理过程,包括获取代理对象、拦截器链的查找和执行、ReflectiveMethodInvocation的创建与调用,以及如何在拦截器链中处理`@Around`注解的执行逻辑。
摘要由CSDN通过智能技术生成

// Make invocation available if necessary.

oldProxy = AopContext.setCurrentProxy(proxy);

setProxyContext = true;

}

// 2.拿到我们被代理的对象实例

target = targetSource.getTarget();

if (target != null) {

targetClass = target.getClass();

}

// Get the interception chain for this method.

// 3.获取拦截器链:例如使用@Around注解时会找到AspectJAroundAdvice,还有ExposeInvocationInterceptor

List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// 4.检查我们是否有任何拦截器(advice)。 如果没有,直接反射调用目标,并避免创建MethodInvocation。

if (chain.isEmpty()) {

Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);

// 5.不存在拦截器链,则直接进行反射调用

retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);

} else {

// We need to create a method invocation…

// 6.如果存在拦截器,则创建一个ReflectiveMethodInvocation:代理对象、被代理对象、方法、参数、

// 被代理对象的Class、拦截器链作为参数创建ReflectiveMethodInvocation

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

// Proceed to the joinpoint through the interceptor chain.

// 7.触发ReflectiveMethodInvocation的执行方法

retVal = invocation.proceed();

}

// Massage return value if necessary.

// 8.必要时转换返回值

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);

}

}

}

3.获取拦截器链:使用 @Around 注解时会找到 AspectJAroundAdvice,还有 ExposeInvocationInterceptor,其中 ExposeInvocationInterceptor 在前,AspectJAroundAdvice 在后。

6.如果存在拦截器,则创建一个 ReflectiveMethodInvocation,代理对象、被代理对象、方法、参数、被代理对象的 Class、拦截器链作为参数。这边 ReflectiveMethodInvocation 已经持有了被代理对象、方法、参数,后续就可以直接使用反射来调用被代理的方法了,见代码块1。

7.触发 ReflectiveMethodInvocation 的执行方法,见代码块2。

代码块1:ReflectiveMethodInvocation 构造函数


protected ReflectiveMethodInvocation(

Object proxy, Object target, Method method, Object[] arguments,

Class<?> targetClass, List interceptorsAndDynamicMethodMatchers) {

this.proxy = proxy;

this.target = target;

this.targetClass = targetClass;

this.method = BridgeMethodResolver.findBridgedMethod(method);

this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);

this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;

}

代码块2:ReflectiveMethodInvocation#proceed()


@Override

public Object proceed() throws Throwable {

// We start with an index of -1 and increment early.

// 1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法)

if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {

return invokeJoinpoint();

}

// 2.每次调用时,将索引的值递增,并通过索引拿到要执行的拦截器

Object interceptorOrInterceptionAdvice =

this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

// 3.判断拦截器是否为InterceptorAndDynamicMethodMatcher类型(动态方法匹配拦截器)

if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {

// Evaluate dynamic method matcher here: static part will already have

// been evaluated and found to match.

// 进行动态匹配。在此评估动态方法匹配器:静态部件已经过评估并且发现匹配。

InterceptorAndDynamicMethodMatcher dm =

(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;

if (dm.methodMatcher.matches(this.method, this.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.

// 4.只是一个普通的拦截器,则触发拦截器链责任链的调用,并且参数为ReflectiveMethodInvocation本身

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

}

}

该方法是一个责任链的方法,会按索引执行所有的拦截器。

1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法),见代码块3。

4.只是一个普通的拦截器,则直接调用它,参数为自己本身,在本文的例子,interceptorsAndDynamicMethodMatchers 有两个拦截器:ExposeInvocationInterceptor 在前,AspectJAroundAdvice 在后,因此首先会触发 ExposeInvocationInterceptor 的 invoke 方法,见代码块4。

代码块3:invokeJoinpoint()


protected Object invokeJoinpoint() throws Throwable {

// 反射执行连接点,也就是原方法,target为被代理的对象实例、method为执行的方法、arguments为方法参数

return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);

}

public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)

throws Throwable {

// Use reflection to invoke the method.

try {

// 使用反射调用方法

ReflectionUtils.makeAccessible(method);

return method.invoke(target, args);

} catch (InvocationTargetException ex) {

// Invoked method threw a checked exception.

// We must rethrow it. The client won’t see the interceptor.

throw ex.getTargetException();

} catch (IllegalArgumentException ex) {

throw new AopInvocationException(“AOP configuration seems to be invalid: tried calling method [” +

method + “] on target [” + target + “]”, ex);

} catch (IllegalAccessException ex) {

throw new AopInvocationException(“Could not access method [” + method + “]”, ex);

}

}

代码块4:ExposeInvocationInterceptor#invoke


@Override

public Object invoke(MethodInvocation mi) throws Throwable {

MethodInvocation oldInvocation = invocation.get();

// 1.设置为当前的MethodInvocation

invocation.set(mi);

try {

// 2.继续进入链中的下一个拦截器。

return mi.proceed();

} finally {

// 3.执行结束设置回原来的MethodInvocation

invocation.set(oldInvocation);

}

}

2.继续进入链中的下一个拦截器,该方法会回到代码块1,从而拿到下一个拦截器,并触发其 invoke 方法,在本例中也就是:AspectJAroundAdvice#invoke,见代码块5。

代码块5:AspectJAroundAdvice#invoke


@Override

public Object invoke(MethodInvocation mi) throws Throwable {

// 1.这边的mi就是我们的ReflectiveMethodInvocation,

// ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,所以这边肯定通过校验

if (!(mi instanceof ProxyMethodInvocation)) {

throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);

}

// 2.将mi直接强转成ProxyMethodInvocation,mi持有代理类实例proxy、被代理类实例target、被代理的方法method等

ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;

// 3.将pmi封装层MethodInvocationProceedingJoinPoint(直接持有入参mi,也就是ReflectiveMethodInvocation的引用)

ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);

// 4.拿到pointcut的表达式

JoinPointMatch jpm = getJoinPointMatch(pmi);

// 5.调用增强方法

return invokeAdviceMethod(pjp, jpm, null, null);

}

5.调用增强方法,见代码块6。

代码块6:invokeAdviceMethod


protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)

throws Throwable {

// 1.argBinding:获取方法执行连接点处的参数

// 2.invokeAdviceMethodWithGivenArgs:使用给定的参数调用增强方法

return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));

}

1.获取方法执行连接点处的参数,见代码块7。

2.使用 argBinding 方法返回的参数调用增强方法,在本文给出的例子中,也就是 LogInterceptor 中被 @Around 修饰的 around(ProceedingJoinPoint pjp) 方法,见代码块8。

代码块7:argBinding


protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {

calculateArgumentBindings();

// AMC start

Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];

int numBound = 0;

if (this.joinPointArgumentIndex != -1) {

// 1.如果存在连接点参数,则将jp添加到调用参数

// 当使用@Around时就有参数;使用@Before、@After时就没有参数

adviceInvocationArgs[this.joinPointArgumentIndex] = jp;

numBound++;

} else if (this.joinPointStaticPartArgumentIndex != -1) {

adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();

numBound++;

}

if (!CollectionUtils.isEmpty(this.argumentBindings)) {

// binding from pointcut match

// 2.使用pointcut匹配绑定

if (jpMatch != null) {

PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();

for (PointcutParameter parameter : parameterBindings) {

String name = parameter.getName();

Integer index = this.argumentBindings.get(name);

adviceInvocationArgs[index] = parameter.getBinding();

numBound++;

}

}

// binding from returning clause

// 3.用于绑定@AfterReturing中的returning参数

if (this.returningName != null) {

Integer index = this.argumentBindings.get(this.returningName);

adviceInvocationArgs[index] = returnValue;

numBound++;

}

// binding from thrown exception

// 4.用于绑定@AfterThrowing中的throwing参数

if (this.throwingName != null) {

Integer index = this.argumentBindings.get(this.throwingName);

adviceInvocationArgs[index] = ex;

numBound++;

}

}

if (numBound != this.parameterTypes.length) {

throw new IllegalStateException("Required to bind " + this.parameterTypes.length +

" arguments, but only bound " + numBound + " (JoinPointMatch " +

(jpMatch == null ? “was NOT” : “WAS”) + " bound in invocation)");

}

return adviceInvocationArgs;

}

1.如果存在连接点参数,则将 jp 添加到增强方法的参数数组,对于 @Around 来说,这边的 jp 就是代码块5中的入参 mi,也就是我们之前创建的 ReflectiveMethodInvocation 对象。所以,当使用 @Around 时,这边返回的增强方法的参数数组持有的是 ReflectiveMethodInvocation 对象。

代码块8:invokeAdviceMethodWithGivenArgs


protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {

Object[] actualArgs = args;

// 1.如果增强方法没有参数,则将actualArgs赋值为null

if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {

actualArgs = null;

}

try {

ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);

// TODO AopUtils.invokeJoinpointUsingReflection

// 2.反射执行增强方法

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();

}

}

2.反射执行增强方法,对于本文给出的例子,这边会直接走到 LogInterceptor 中被 @Around 修饰的 around(ProceedingJoinPoint pjp) 方法,该方法会执行一些增强逻辑,最终执行 “Object result = pjp.proceed()”。

通过代码块6和代码块7我们知道,这边的 pjp 就是我们之前创建的 ReflectiveMethodInvocation 对象,所以这边会再次调用 ReflectiveMethodInvocation 对象的 process() 方法,也就是回到代码块2。此时我们的拦截器都已经执行完毕,因此会走到 invokeJoinpoint() 方法,通过反射执行我们被代理的方法,也就是 getName(String name) 方法。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

最新整理面试题
在这里插入图片描述

上述的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题

最新整理电子书

在这里插入图片描述

最新整理大厂面试文档

在这里插入图片描述

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

最新整理面试题
[外链图片转存中…(img-MahNhF1i-1713385360205)]

上述的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题

最新整理电子书

[外链图片转存中…(img-kU7Ok35L-1713385360205)]

最新整理大厂面试文档

[外链图片转存中…(img-UZO2jXN9-1713385360206)]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值