Spring 源码阅读 62:基于 JDK 的 AOP 代理对特殊方法调用的处理

简介: 本文深入分析了 JdkDynamicAopProxy 的invoke方法的第一部分逻辑,也就是,对于哪些方法的调用,不对其进行增强,而是执行对应的特定逻辑,从中可以了解,Spring AOP 对哪些方法是默认不进行增强的。

基于 Spring Framework v5.2.6.RELEASE

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

概述

上一篇分析了 JdkDynamicAopProxy 的invoke方法对方法调用进行增强的整理流程,这篇开始进行细节的深入分析,本文分析第一个关键点,特殊方法调用的处理。

特殊方法调用的处理

进入invoke方法的第一段主要逻辑。

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

这里处理了四种特殊情况的方法调用,并且每一段处理流程都会返回结果,也就是结束invoke方法,不再执行后面的逻辑。下面我们分别分析这几种情况。

equals 和 hashCode 方法

首先分析if-else语句的第一个分支,它的判断条件是:

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

判断条件有两个,第二个比较好理解,就是判断当前调用的目标方法,是不是 Object 类中声明的equals方法或者其在子类中的实现,判断的根据是方法名、参数个数和参数类型,具体的代码比较简单,就不列在这里了。

下面重点分析一下equalsDefined成员变量的值,如果你读过之前的文章中对代理对象创建过程的分析,应该对这个变量有印象。在 JdkDynamicAopProxy 的getProxy方法中,会调用一个名叫findDefinedEqualsAndHashCodeMethods方法,来检查目标类实现的接口中,是否定义了equalshashCode方法,我们再回顾一下这个方法的源码。

// org.springframework.aop.framework.JdkDynamicAopProxy#findDefinedEqualsAndHashCodeMethodsprivatevoidfindDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
for (Class<?>proxiedInterface : proxiedInterfaces) {
Method[] methods=proxiedInterface.getDeclaredMethods();
for (Methodmethod : methods) {
if (AopUtils.isEqualsMethod(method)) {
this.equalsDefined=true;
         }
if (AopUtils.isHashCodeMethod(method)) {
this.hashCodeDefined=true;
         }
if (this.equalsDefined&&this.hashCodeDefined) {
return;
         }
      }
   }
}

这个方法中会遍历所有的代理接口中声明的所有方法,只要这些方法中有equalshashCode方法,则将 JdkDynamicAopProxy 中对应的成员变量equalsDefinedhashCodeDefined的值设置为true

详细的分析可以参考:Spring 源码阅读 60:通过 JDK 动态代理或者 CGLIB 创建 AOP 代理对象

回到前面的判断条件中,如果当前被调用的方法是equals方法,并且目标类的代理接口列表中找不到同样的方法定义,则判断条件的结果为true。这也意味着当前调用的equals方法是 Object 中的equals方法或者其重写方法,那么会调用 JdkDynamicAopProxy 中的equals方法,如果目标类实现的接口中定义了相同签名的equals方法,则会按照一般的方法进入后续的流程。

我们看一下 JdkDynamicAopProxy 中的equals方法。

// org.springframework.aop.framework.JdkDynamicAopProxy#equals@Overridepublicbooleanequals(@NullableObjectother) {
if (other==this) {
returntrue;
   }
if (other==null) {
returnfalse;
   }
JdkDynamicAopProxyotherProxy;
if (otherinstanceofJdkDynamicAopProxy) {
otherProxy= (JdkDynamicAopProxy) other;
   }
elseif (Proxy.isProxyClass(other.getClass())) {
InvocationHandlerih=Proxy.getInvocationHandler(other);
if (!(ihinstanceofJdkDynamicAopProxy)) {
returnfalse;
      }
otherProxy= (JdkDynamicAopProxy) ih;
   }
else {
// Not a valid comparison...returnfalse;
   }
// If we get here, otherProxy is the other AopProxy.returnAopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
}

这里的逻辑就比较简单了,就是对目标对象对应的 JdkDynamicAopProxy 进行了比较。

至此分析完了equals方法的处理逻辑,两外一个特殊方法hashCode与此相似,就不重复分析了。

声明在 DecoratingProxy 中的方法

再来看第三个特殊情况。

if (method.getDeclaringClass() ==DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.returnAopProxyUtils.ultimateTargetClass(this.advised);
}

这里的判断条件意思是,如果当前调用的目标方法,是在 DecoratingProxy 声明的,则执行语句块中的逻辑。我们看一下 DecoratingProxy 的源码。

publicinterfaceDecoratingProxy {
Class<?>getDecoratedClass();
}

这个接口中只声明了一个方法,因此,判断条件其实就是判断方法是不是这个方法的实现方法。回顾 JdkDynamicAopProxy 的getProxy方法,在创建最终的代理对象之前,会在代理接口列表中添加 DecoratingProxy。

如果getDecoratedClass方法是当前被调用的方法,则会执行代码块中的逻辑,调用 AopProxyUtils 的ultimateTargetClass方法。我们进入这个方法查看源码。

// org.springframework.aop.framework.AopProxyUtils#ultimateTargetClasspublicstaticClass<?>ultimateTargetClass(Objectcandidate) {
Assert.notNull(candidate, "Candidate object must not be null");
Objectcurrent=candidate;
Class<?>result=null;
while (currentinstanceofTargetClassAware) {
result= ((TargetClassAware) current).getTargetClass();
current=getSingletonTarget(current);
   }
if (result==null) {
result= (AopUtils.isCglibProxy(candidate) ?candidate.getClass().getSuperclass() : candidate.getClass());
   }
returnresult;
}

其主要逻辑就是在一个while循环中,遍历嵌套的代理层,获取到最终的目标类。因此,我们也知道了,DecoratingProxy 接口被添加到代理接口列表中的作用就是能够通过一个方法获取到最终被代理的类信息。

声明在 Advised 中的方法

最后我们再看第四个特殊情况的处理。

if (!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);
}

这个判断条件中,this.advised.opaque的值默认是false,因此,判断条件就是当前调用的目标方法是不是 Advised 接口中声明的方法,如果是,则执行 AopUtils 的invokeJoinpointUsingReflection方法。

Advised 中声明的方法比较多,这些方法都是用来修改 AOP 代理的配置。因此,对于这些方法的调用,都会直接通过反射调用目标方法,而不进行增强。

总结

本文深入分析了 JdkDynamicAopProxy 的invoke方法的第一部分逻辑,也就是,对于哪些方法的调用,不对其进行增强,而是执行对应的特定逻辑,从中可以了解,Spring AOP 对哪些方法是默认不进行增强的。

目录
相关文章
|
2天前
|
Java Spring
【JavaEE进阶】 Spring AOP源码简单剖析
【JavaEE进阶】 Spring AOP源码简单剖析
|
2天前
|
Java Spring
【JavaEE进阶】 Spring AOP详解
【JavaEE进阶】 Spring AOP详解
|
2天前
|
数据采集 Java 程序员
【JavaEE进阶】 Spring AOP快速上手
【JavaEE进阶】 Spring AOP快速上手
|
7天前
|
Java Spring
|
8天前
|
Java Spring
|
21天前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
27 0
|
21天前
AOP&面向切面编程
AOP&面向切面编程
60 0
|
21天前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
|
21天前
|
数据库
AOP(面向切面编程)的基本概念和原理
AOP(面向切面编程)的基本概念和原理
134 0
|
21天前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
26 1