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方法的整体流程,下一篇开始将会深入分析其中的原理细节。

目录
相关文章
|
18天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
21天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
3天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
12 1
|
4天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
45 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
12天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
16天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1
|
16天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
22天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
53 0
|
2月前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
137 0