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

目录
相关文章
|
2月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
61 1
|
17天前
|
XML Java 数据格式
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
12 0
|
18天前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
|
20天前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
14 0
|
21天前
|
缓存 Java Maven
JDK 动态代理
JDK 动态代理
8 0
|
26天前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
20 0
|
2月前
|
设计模式 Java
JDK动态代理
JDK动态代理
|
2月前
|
Java 程序员 API
浅谈JDK动态代理
浅谈JDK动态代理
51 1
|
2月前
|
NoSQL 关系型数据库 MySQL
Docker安装详细步骤及相关环境安装配置(mysql、jdk、redis、自己的私有仓库Gitlab 、C和C++环境以及Nginx服务代理)
Docker安装详细步骤及相关环境安装配置(mysql、jdk、redis、自己的私有仓库Gitlab 、C和C++环境以及Nginx服务代理)
449 0
|
2月前
|
设计模式 Java API
[Java]静态代理、动态代理(基于JDK1.8)
本篇文章主要是对静态代理和动态代理实现思路的简述,以示例为主,少涉及理论。 如果文中阐述不全或不对的,多多交流。
65 1
[Java]静态代理、动态代理(基于JDK1.8)