Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

简介: 本文分析了 JdkDynamicAopProxy 的invoke方法,invoke方法是基于 JDK 动态代理创建的 AOP 代理对象的方法处理回调逻辑,也是 Spring AOP 增强目标方法的关键逻辑。

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 60:通过 JDK 动态代理或者 CGLIB 创建 AOP 代理对象

概述

上一篇中,分析了 Spring AOP 通过 JDK 动态代理和 CGLIB 创建代理对象的逻辑。在 JDK 动态代理的方式中,会创建一个 JdkDynamicAopProxy 作为代理对象的 InvocationHandler 来处理对目标方法的增强。本文开始,我们将进入 JdkDynamicAopProxy 的invoke方法,看看增强逻辑是如何执行的。

invoke 方法

找到 JdkDynamicAopProxy 类型的invoke方法源码。

image.png

方法比较长,我们可以先分析整体结构,其中主要的逻辑都在try语句块中,我们分成几个部分来分析具体的逻辑。

advised 成员变量的来源

ObjectoldProxy=null;
booleansetProxyContext=false;
TargetSourcetargetSource=this.advised.targetSource;
Objecttarget=null;

方法的开始,定义了一些后面会用到的局部变量,这里需要留意一下targetSource这个局部变量,其他几个只是给了一个简单的初始值,后面流程中遇到了再分析。

targetSource的值是this.advised.targetSource,我们先找到advised环境变量。

/** Config used to configure this proxy. */privatefinalAdvisedSupportadvised;

它的类型是 AdvisedSupport,注释中说明了它是对代理对象的配置。前面我们分析过 JdkDynamicAopProxy 对象的创建,创建的方式是调用它的构造方法。

publicJdkDynamicAopProxy(AdvisedSupportconfig) throwsAopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length==0&&config.getTargetSource() ==AdvisedSupport.EMPTY_TARGET_SOURCE) {
thrownewAopConfigException("No advisors and no TargetSource specified");
   }
this.advised=config;
}

advised正是在构造方法中创建的,而方法参数传入的config参数,就是创建代理对象之前初始化好的 ProxyFactory 对象。

这一部分的详细分析可以参考:Spring 源码阅读 59:确定创建 AOP 代理的方式是 JDK 动态代理还是 CGLIB

advisedtargetSource属性,则是初始化 ProxyFactory 时为其配置的属性,其中封装了目标 Bean 的信息。

特殊情况的处理

回到invoke方法中接着往下看,进入try语句块中的逻辑。

if (!this.equalsDefined&&AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.returnequals(args[0]);
}
elseif (!this.hashCodeDefined&&AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.returnhashCode();
}
elseif (method.getDeclaringClass() ==DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.returnAopProxyUtils.ultimateTargetClass(this.advised);
}
elseif (!this.advised.opaque&&method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...returnAopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

这里通过对参数中传入的method变量的判断,对被调用的目标方法的四种特定情况进行了处理,如果执行的方法调用,不在这四种特殊情况当中,则执行后面的一般情况处理逻辑。

if (this.advised.exposeProxy) {
// Make invocation available if necessary.oldProxy=AopContext.setCurrentProxy(proxy);
setProxyContext=true;
}

接下来,如果exposeProxy的值为true的话,将代理对象放到当前的 AopContext 上下文中。不过,exposeProxy默认值是false,因此只段逻辑一般不会执行到。

获取拦截器链

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

获取被代理的目标对象和目标对象的类型。并且,从advised中获取到增强目标方法的拦截器链。这里调用的getInterceptorsAndDynamicInterceptionAdvice方法是invoke方法的流程中的关键点之一。获取到拦截器链之后,就是拦截器中的增强逻辑的执行。

执行增强逻辑

// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a 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...MethodInvocationinvocation=newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.retVal=invocation.proceed();
}

这里分成了两种情况,就是拦截器链是不是为空的情况。

这里需要说明一下拦截器链为空的情况。在创建 AOP 代理之前,已经根据目标类型,进行过了拦截器的匹配(也就是 Advisor 的筛选),那为什么还会出现这里找到的拦截器链为空的情况呢?在创建代理的时候,因为被代理的是一个类,只要这个类中,有一个方法需要被增强,这个类就需要被代理。但是,具体到这个类中的每一方法,并不是需要被代理的类中的每一个方法都会被增强,因此,这里需要分情况处理。

返回执行结果

// 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;
}
elseif (retVal==null&&returnType!=Void.TYPE&&returnType.isPrimitive()) {
thrownewAopInvocationException(
"Null return value from advice does not match primitive return type for: "+method);
}
returnretVal;

方法的最后一部分,就是将最终执行的结果返回。

总结

本文分析了 JdkDynamicAopProxy 的invoke方法,invoke方法是基于 JDK 动态代理创建的 AOP 代理对象的方法处理回调逻辑,也是 Spring AOP 增强目标方法的关键逻辑。通过本文,可以了解invoke方法的整体流程,下一篇开始将会深入分析其中的原理细节。

目录
相关文章
|
17小时前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
21 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
6天前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
15 2
|
6天前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
19 2
|
3月前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
26 0
|
4月前
AOP&面向切面编程
AOP&面向切面编程
56 0
|
4月前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
|
4月前
|
数据库
AOP(面向切面编程)的基本概念和原理
AOP(面向切面编程)的基本概念和原理
91 0
|
6月前
|
缓存 监控 Java
Spring框架之AOP(面向切面编程)
Spring框架之AOP(面向切面编程)
34 0
|
7月前
|
前端开发 Java 数据库连接
Spring Aop:面向切面编程
Spring Aop:面向切面编程
39 0
|
1月前
|
Java Spring
代码优雅的转变:基于注解的AOP编程在Spring中的实践
代码优雅的转变:基于注解的AOP编程在Spring中的实践
17 0