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

简介: 本文分别分析了 Spring 通过 JDK 动态代理和 CGLIB 两种方式创建 AOP 代理对象的过程。至此,Spring AOP 特性中,代理对象创建的全部过程就分析完了。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 59:确定创建 AOP 代理的方式是 JDK 动态代理还是 CGLIB

概述

上一篇分析了创建代理对象的getProxy方法,以及 Spring 如何选择使用 JDK 动态代理还是 CGLIB 来创建代理对象。本文来分析这两种方式创建代理对象的过程。

通过 JDK 动态代理创建代理对象

首先来看 Spring 通过 JDK 动态代理的方式创建 AOP 代理对象的过程,也就是 JdkDynamicAopProxy 的getProxy方法。

// org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)@OverridepublicObjectgetProxy(@NullableClassLoaderclassLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: "+this.advised.getTargetSource());
   }
Class<?>[] proxiedInterfaces=AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
returnProxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

首先,会通过 AopProxyUtils 的completeProxiedInterfaces方法,获取要代理的接口数组。调用方法时,传入的参数this.advised成员变量,是在 JdkDynamicAopProxy 的构造方法中初始化的。

// org.springframework.aop.framework.JdkDynamicAopProxy#JdkDynamicAopProxypublicJdkDynamicAopProxy(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,而这个config就是创建 JdkDynamicAopProxy 时候的 ProxyFactory 对象。completeProxiedInterfaces方法的作用是生成完整的代理接口列表,其中包括 ProxyFactory 中配置的 Bean 类型实现的所有接口,还会增加 SpringProxy 和 Advised 接口,第二个参数传入了true,还会增加 DecoratingProxy 接口。

接下来调用了findDefinedEqualsAndHashCodeMethods方法。我们看一下这个方法的源码。

// 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方法,则将对应的成员变量equalsDefinedhashCodeDefined的值设置为true。这一步骤的作用尚不知道,不过既然这里这两个成员变量的值可能会在此处被修改,那么之后肯定还会遇到,到时候我们再分析它的作用。

最后一步就是代理对象的创建,调用了 JDK 动态代理的方法,创建代理对象并返回。

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

这个方法的内容,已经不属于 Spring 框架的范畴,就不深入分析了,我们找到这个方法的定义,大概了解一下这几个参数。

@CallerSensitivepublicstaticObjectnewProxyInstance(ClassLoaderloader,
Class<?>[] interfaces,
InvocationHandlerh) {
// 省略方法体中的代码}

第一个参数loader是创建代理的类加载器,第二个参数interfaces是代理对象要实现的所有接口,也就是上一步我们得到的代理接口列表。这两个参数比较好理解。

第三个参数的类型是 InvocationHandler,这个参数方法调用的处理器,也就是对代理对象的方法调用回分派到这个 InvocationHandler 对象中,在上一步中通过newProxyInstance创建代理对象的时候,这个参数传入的是this,也就是在 DefaultAopProxyFactory 中创建的 JdkDynamicAopProxy 对象,也就是说,对代理对象的方法调用,都是由它来完成的。

image.png

从上面的接口中,可以看到 JdkDynamicAopProxy 是实现了 InvocationHandler 接口的,主要的处理逻辑在 InvocationHandler 接口定义的invoke方法中。

通过 CGLIB 创建代理对象

下面接着分析 CGLIB 代理对象的创建,也就是 ObjenesisCglibAopProxy 的getProxy方法,在此之前,我们先看一下它的构造方法,看看代理对象被创建之前,创建 ObjenesisCglibAopProxy 的时候执行了哪些工作。

// org.springframework.aop.framework.ObjenesisCglibAopProxy#ObjenesisCglibAopProxypublicObjenesisCglibAopProxy(AdvisedSupportconfig) {
super(config);
}

只是调用了父类的构造方法,我们再跟着找到父类的构造方法。

// org.springframework.aop.framework.CglibAopProxy#CglibAopProxypublicCglibAopProxy(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;
this.advisedDispatcher=newAdvisedDispatcher(this.advised);
}

这里初始化了两个成员变量,初始化advised的方式跟 JdkDynamicAopProxy 一样,而advisedDispatcher成员变量的初始化,是通过其构造方法创建了一个 AdvisedDispatcher 的对象。

接下来再分析getProxy方法,这个方法的实现也是在父类 CglibAopProxy 中,我们找到方法的代码。

image.png

方法的代码比较多,我们分步来分析,方法的逻辑都在try语句块中。

Class<?>rootClass=this.advised.getTargetClass();

首先要做的就是获取到要代理的目标类型。

Class<?>proxySuperClass=rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass=rootClass.getSuperclass();
Class<?>[] additionalInterfaces=rootClass.getInterfaces();
for (Class<?>additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
   }
}

然后获取目标类型实现的接口,并将它们添加到advised成员变量配置的接口列表中。

// Configure CGLIB Enhancer...Enhancerenhancer=createEnhancer();
if (classLoader!=null) {
enhancer.setClassLoader(classLoader);
if (classLoaderinstanceofSmartClassLoader&&         ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
   }
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(newClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks=getCallbacks(rootClass);
Class<?>[] types=newClass<?>[callbacks.length];
for (intx=0; x<types.length; x++) {
types[x] =callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(newProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);

接下来就是创建 Enhancer,并设置它的一些属性。Enhancer 字节码增强器是 CGLIB 创建代理对象的一个重要的类。这里设置了类加载器、父类(也就是代理的目标类型)、回调过滤器、回调类型数组等。

returncreateProxyClassAndInstance(enhancer, callbacks);

最后,通过createProxyClassAndInstance方法创建了代理对象,我们再进入createProxyClassAndInstance方法看一下。

image.png

这里需要注意,因为之前创建的 AopProxy 对象是 ObjenesisCglibAopProxy 类型,因此,ObjenesisCglibAopProxy 类型中实现了这个方法,我们应该看 ObjenesisCglibAopProxy 中的实现逻辑。

其中比较关键的逻辑,是通过enhancer.createClass()来创建代理类型,以及通过objenesis.newInstance来创建代理对象实例,不过,这些都属于 CGLIB 的内容,不在 Spring 框架的范畴之内,这里不做介绍了。

总结

本文分别分析了 Spring 通过 JDK 动态代理和 CGLIB 两种方式创建 AOP 代理对象的过程。至此,Spring AOP 特性中,代理对象创建的全部过程就分析完了。


目录
相关文章
|
2月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
48 1
|
3月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
43 0
|
3月前
|
XML Java 数据格式
浅谈基于动态代理的Spring AOP原理
浅谈基于动态代理的Spring AOP原理
26 0
|
3月前
|
Java 开发者
JDK 21中的记录模式(Record Patterns):简化对象匹配与解构
本文将详细介绍JDK 21中引入的新特性——记录模式(Record Patterns)。记录模式是一种强大的语言特性,它允许开发者在switch表达式中使用简化的语法来匹配和解构记录类型(record types)。本文将解释记录模式的概念、语法、使用场景以及与传统模式匹配的区别,并通过示例代码展示记录模式在实际开发中的应用。
|
3月前
|
设计模式 安全 Java
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
386 1
|
3月前
Spring5源码(27续)-cglib原理解析
Spring5源码(27续)-cglib原理解析
15 0
|
4月前
|
设计模式 Java Spring
Spring AOP基础之代理模式.静态代理和动态代理
Spring AOP基础之代理模式.静态代理和动态代理
31 0
Spring AOP基础之代理模式.静态代理和动态代理
|
4月前
|
Java 数据安全/隐私保护
【面试问题】JDK 动态代理与 CGLIB 区别?
【1月更文挑战第27天】【面试问题】JDK 动态代理与 CGLIB 区别?
|
3天前
|
IDE Java Shell
02|手把手教你安装JDK与配置主流IDE
02|手把手教你安装JDK与配置主流IDE
6 0
|
5天前
|
Java Shell 开发者
都2024年了!你还不知道在Docker中安装jdk?
都2024年了!你还不知道在Docker中安装jdk?