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

本文详细介绍了Java中JDK动态代理和CGLIB代理的`invoke`方法,重点讲解了JdkDynamicAopProxy#invoke的执行流程,包括目标对象处理、拦截器链的获取和执行。涉及的关键概念有AOP代理、MethodInvocation和AspectJ的aroundadvice。
摘要由CSDN通过智能技术生成

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

当我们调用了被 AOP 代理的方法时,使用 JDK 动态代理会走到 JdkDynamicAopProxy#invoke 方法,使用 CBLIB 代理会走到 DynamicAdvisedInterceptor#intercept 方法,两者的内容基本一样,这里就拿更常见的 JdkDynamicAopProxy#invoke 方法来介绍。

JdkDynamicAopProxy#invoke


@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

MethodInvocation invocation;

Object oldProxy = null;

boolean setProxyContext = false;

// 1.advised就是proxyFactory,而targetSource持有被代理对象的引用

TargetSource targetSource = this.advised.targetSource;

Class<?> targetClass = null;

Object target = null;

try {

if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {

// The target does not implement the equals(Object) method itself.

// 目标不实现equals(Object)方法本身。

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.

// 只有getDecoratedClass()声明 - > dispatch到代理配置。

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…

// ProxyConfig上的服务调用与代理配置…

return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);

}

Object retVal;

// 有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理

if (this.advised.exposeProxy) {

// 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++;

}

}

最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性

Java面试宝典2021版

最常见Java面试题解析(2021最新版)

2021企业Java面试题精选

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

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

adviceInvocationArgs[index] = ex;

numBound++;

}

}

最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性

Java面试宝典2021版

[外链图片转存中…(img-0KBWYNI6-1714746272333)]

[外链图片转存中…(img-uAwSfoRQ-1714746272334)]

最常见Java面试题解析(2021最新版)

[外链图片转存中…(img-Wrw351HV-1714746272334)]

[外链图片转存中…(img-Zjvh6U4Y-1714746272334)]

2021企业Java面试题精选

[外链图片转存中…(img-Dm4O9ImM-1714746272334)]

[外链图片转存中…(img-r0wKOCvg-1714746272335)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值