likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 68

概述

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

特殊方法调用的处理

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

if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
   // The target does not implement the equals(Object) method itself.
   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.
   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...
   return AopUtils.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#findDefinedEqualsAndHashCodeMethods
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
   for (Class<?> proxiedInterface : proxiedInterfaces) {
      Method[] methods = proxiedInterface.getDeclaredMethods();
      for (Method method : 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

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

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

// org.springframework.aop.framework.JdkDynamicAopProxy#equals
@Override
public boolean equals(@Nullable Object other) {
   if (other == this) {
      return true;
   }
   if (other == null) {
      return false;
   }

   JdkDynamicAopProxy otherProxy;
   if (other instanceof JdkDynamicAopProxy) {
      otherProxy = (JdkDynamicAopProxy) other;
   }
   else if (Proxy.isProxyClass(other.getClass())) {
      InvocationHandler ih = Proxy.getInvocationHandler(other);
      if (!(ih instanceof JdkDynamicAopProxy)) {
         return false;
      }
      otherProxy = (JdkDynamicAopProxy) ih;
   }
   else {
      // Not a valid comparison...
      return false;
   }

   // If we get here, otherProxy is the other AopProxy.
   return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
}

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

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

声明在 DecoratingProxy 中的方法

再来看第三个特殊情况。

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

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

public interface DecoratingProxy {

Class<?> getDecoratedClass();

}

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

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

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

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

声明在 Advised 中的方法

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

if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
      method.getDeclaringClass().isAssignableFrom(Advised.class)) {
   // Service invocations on ProxyConfig with the proxy config...
   return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

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

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

总结

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

转载自:https://juejin.cn/post/7158771444397113351
评论
请登录