☰
Spring AOP 源码阅读:AopProxyFactory 代理工厂接口与 JDK/CGLIB 代理选择机制
2026/10/2 2:23:40 网站建设 项目流程
  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载

AopProxyFactory是 Spring AOP 框架中负责"生产代理对象"的工厂接口,它接收一份AdvisedSupport形式的 AOP 配置,根据目标类型与配置标志决定采用 JDK 动态代理还是 CGLIB 代理,并返回对应的AopProxy。本文基于 spring-reading 仓库中 spring-aop-aopProxyFactory 模块,从接口契约、默认实现、决策源码到可运行的实践代码,完整梳理 AopProxyFactory 的工作原理,读完你既能看懂 Spring 代理创建的分支逻辑,也能独立写出 JDK/CGLIB 两种代理的创建程序。

一、AopProxyFactory 在 Spring AOP 中的定位

在 Spring AOP 的完整链路中,Advisor(通知器)描述"在哪里织入、织入什么",Pointcut(切点)决定"哪些方法被匹配",而真正把切面逻辑"装配"到目标对象上、生成一个可用的增强对象的,正是 AOP 代理。AopProxyFactory接口正是这一环节的工厂入口。

根据 AopProxyFactory README 的说明,其核心职责可以概括为三点:

  1. 创建 AOP 代理对象:根据传入的AdvisedSupport对象,利用指定的代理方式(JDK 动态代理或 CGLIB 代理)生成 AOP 代理对象;
  2. 决定代理方式:根据目标类的类型和配置信息,确定使用 JDK 动态代理还是 CGLIB 代理,这个决定通常基于配置中的一些条件,例如是否需要代理接口、是否允许使用 CGLIB 代理;
  3. 支持灵活配置:通过实现该接口,可以灵活地定制 AOP 代理的生成方式,以满足不同场景下的需求。

在仓库的模块划分中,spring-aop-aopProxyFactory 模块的src/main/java/com/xcs/spring/下提供了AopProxyFactoryDemo.java、MyService.java、MyServiceImpl.java三个文件,分别对应演示入口、目标接口与目标实现类,是理解该接口最直接的落地样例。

二、接口契约:AopProxyFactory 源码逐行解读

AopProxyFactory是"由能够基于AdvisedSupport配置对象创建 AOP 代理的工厂实现"的接口。它只有一个方法:

public interface AopProxyFactory { /** * 根据给定的 AOP 配置创建一个 {@link AopProxy}。 * @param config 以 AdvisedSupport 对象形式表示的 AOP 配置 * @return 相应的 AOP 代理 * @throws AopConfigException 如果配置无效 */ AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException; }

2.1 入参 AdvisedSupport:AOP 配置的载体

AdvisedSupport是 Spring AOP 中"可被配置的 AOP 配置对象",它聚合了目标对象(TargetSource)、代理接口(proxiedInterfaces)、通知(Advisor/Advice 列表)以及一系列代理开关标志。在工厂方法中它扮演"配置快照"的角色,工厂实现据此读取:

  • config.getTargetClass():目标类的类型;
  • config.getProxiedInterfaces():需要被代理的接口数组;
  • config.isOptimize():优化标志;
  • config.isProxyTargetClass():是否强制代理目标类(而非仅代理接口);
  • config.isFrozen()、config.isExposeProxy()等:影响代理内部回调构造的辅助标志。

2.2 返回值 AopProxy:真正的代理抽象

createAopProxy返回的是AopProxy接口(而非最终代理对象本身)。AopProxy是一个"配置 AOP 代理的委托接口,允许创建实际的代理对象",它提供两个getProxy()方法:

public interface AopProxy { /** * 创建一个新的代理对象,使用 AopProxy 的默认类加载器(通常为线程上下文类加载器)。 */ Object getProxy(); /** * 使用给定的类加载器创建代理对象;若传入 null 则交由低级代理工具决定默认值。 */ Object getProxy(@Nullable ClassLoader classLoader); }

也就是说,AopProxyFactory负责"选型与装配",AopProxy负责"真正产出代理实例",两者分工明确。更深一层的JdkDynamicAopProxy与CglibAopProxy源码分析,可以继续阅读仓库中的 AopProxy 源码分析。

2.3 代理对象必须遵守的约定

接口 Javadoc 对代理对象提出了明确的质量要求,这也是实现者(以及自定义实现)必须满足的契约:

  • 代理对象应实现配置中指示"应该被代理"的所有接口;
  • 代理对象应实现Advised接口(以便在运行期查询/修改通知);
  • 代理对象应实现equals方法,用于比较被代理的接口、通知和目标;
  • 如果所有通知者和目标都是可序列化的,代理对象应该是可序列化的;
  • 如果通知者和目标都是线程安全的,代理对象应该是线程安全的。

此外,代理可能允许或不允许更改通知——如果配置已被冻结(frozen)而不允许更改通知,代理在尝试更改通知时应抛出AopConfigException。这也是为什么DefaultAopProxyFactory在配置无效(例如目标类为空)时会抛出AopConfigException的原因所在。

三、默认实现 DefaultAopProxyFactory:JDK 还是 CGLIB?

DefaultAopProxyFactory是AopProxyFactory接口的默认实现类(同时实现了Serializable),位于org.springframework.aop.framework包。根据 AopProxyFactory README 第八节给出的源码,其createAopProxy方法的完整决策逻辑如下:

@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { // 检查是否支持 CGLIB 代理,如果是,则创建 CGLIB 代理 if (!NativeDetector.inNativeImage() && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { // 获取目标类 Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { // 如果目标类为空,则抛出 AopConfigException 异常 throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } // 如果目标类是接口或者是代理类,则创建 JDK 动态代理 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 否则,创建 CGLIB 代理 return new ObjenesisCglibAopProxy(config); } else { // 否则,创建 JDK 动态代理 return new JdkDynamicAopProxy(config); } } /** * 确定提供的 AdvisedSupport 是否仅指定了 SpringProxy 接口(或者根本没有指定代理接口)。 */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); }

3.1 决策分支拆解

DefaultAopProxyFactory的 Javadoc 明确说明:如果对于给定的AdvisedSupport实例满足以下条件之一,则创建 CGLIB 代理:

条件对应标志/方法说明
设置了optimize标志config.isOptimize()主动要求 Spring 采取优化措施,通常由@EnableAspectJAutoProxy的optimize属性等驱动
设置了proxyTargetClass标志config.isProxyTargetClass()强制代理目标类本身(而非接口),是强制 CGLIB 的最常见方式
未指定代理接口hasNoUserSuppliedProxyInterfaces(config)只代理SpringProxy接口或完全没有用户接口时,只能靠子类化目标类

但即便进入"倾向 CGLIB"的分支,代码还会再做一次兜底校验:

  • 目标类为null→ 抛出AopConfigException,提示"Either an interface or a target is required for proxy creation";
  • 目标类本身是接口(targetClass.isInterface())或已经是代理类(Proxy.isProxyClass(targetClass))→ 只能退回JdkDynamicAopProxy,因为 CGLIB 无法对接口或已代理类再生成子类;
  • 否则 → 返回ObjenesisCglibAopProxy(CglibAopProxy的子类,借助 Objenesis 绕过构造器限制实例化)。

hasNoUserSuppliedProxyInterfaces的判断很巧妙:它只关心"用户显式提供的代理接口",当接口数组为空、或仅包含框架自身的SpringProxy接口时,都视为"没有用户接口",从而走 CGLIB 分支。

3.2 日常实践中的两个结论

  • 指定一个或多个接口 → JDK 动态代理:只要AdvisedSupport通过setInterfaces(...)设置了业务接口,且未设置proxyTargetClass,就会走JdkDynamicAopProxy;
  • 指定proxyTargetClass→ 强制 CGLIB:即使目标类实现了接口,只要setProxyTargetClass(true),也会优先尝试 CGLIB 子类代理。

四、两种代理实现:JdkDynamicAopProxy 与 CglibAopProxy

AopProxyFactory的两种产出分别对应 AopProxy README 中介绍的两个AopProxy实现:

1. JdkDynamicAopProxy(JDK 动态代理)

  • 当目标对象实现了至少一个接口时使用,基于java.lang.reflect.Proxy生成代理;
  • 核心逻辑:getProxy(classLoader)中调用Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this),自身实现InvocationHandler,在invoke方法中处理equals/hashCode、目标获取、拦截器链构建与执行(详见其源码分析章节)。

2. CglibAopProxy(CGLIB 代理)

  • 当目标类没有实现任何接口(或强制proxyTargetClass)时使用,通过生成目标类的子类来实现拦截;
  • 核心逻辑:在getProxy(classLoader)中配置 CGLIBEnhancer(设置父类为目标类根类、补全代理接口、SpringNamingPolicy命名策略、回调过滤器与回调类型),最终由createProxyClassAndInstance(enhancer, callbacks)生成代理实例;方法拦截入口是内部的DynamicAdvisedInterceptor#intercept。

两者在方法调用时都会通过AdvisorChainFactory获取拦截器链(getInterceptorsAndDynamicInterceptionAdvice),再借助ReflectiveMethodInvocation/CglibMethodInvocation沿链执行,这部分可延伸阅读 AdvisorChainFactory 源码分析 与 ProxyMethodInvocation 源码分析。

五、最佳实践:亲手创建 JDK 与 CGLIB 代理

仓库 AopProxyFactoryDemo.java 给出了完整的可运行示例。目标接口与实现类定义如下:

// MyService.java —— 目标接口 public interface MyService { String doSomething(); } // MyServiceImpl.java —— 目标实现类 public class MyServiceImpl implements MyService { @Override public String doSomething() { return "hello world"; } }

演示入口同时跑出两种代理:

public class AopProxyFactoryDemo { public static void main(String[] args) { // 分别演示 JDK 动态代理和 CGLIB 代理 jdkProxy(); cglibProxy(); } /** * JDK 动态代理示例 */ private static void jdkProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 设置目标对象的类 advisedSupport.setTargetClass(MyService.class); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 JDK 动态代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("jdkProxy = " + myService.getClass()); } /** * CGLIB 代理示例 */ private static void cglibProxy() { // 创建 AdvisedSupport 对象,用于配置 AOP 代理 AdvisedSupport advisedSupport = new AdvisedSupport(); // 设置目标对象 advisedSupport.setTarget(new MyServiceImpl()); // 创建 DefaultAopProxyFactory 实例 AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); // 创建 CGLIB 代理对象 MyService myService = (MyService) aopProxyFactory.createAopProxy(advisedSupport).getProxy(); // 打印生成的代理类 System.out.println("cglibProxy = " + myService.getClass()); } }

5.1 两段代码的差异解读

  • jdkProxy():额外调用了advisedSupport.setTargetClass(MyService.class),但这里传入的是接口类型MyService。回顾createAopProxy的决策逻辑:虽然setTargetClass会间接影响目标类的判定,但此处getTargetClass()返回的是接口MyService,满足targetClass.isInterface(),因此仍走JdkDynamicAopProxy;
  • cglibProxy():只设置了目标对象new MyServiceImpl(),没有指定任何代理接口。此时getProxiedInterfaces()为空数组,hasNoUserSuppliedProxyInterfaces返回true,命中 CGLIB 分支;同时目标类MyServiceImpl既不是接口也不是代理类,于是生成ObjenesisCglibAopProxy。

5.2 运行结果

运行AopProxyFactoryDemo后,控制台输出两种代理类:

jdkProxy = class com.sun.proxy.$Proxy0 cglibProxy = class com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5
  • com.sun.proxy.$Proxy0:JDK 动态代理在运行时生成的代理类,命名由 JVM 统一管理;
  • com.xcs.spring.MyServiceImpl$$EnhancerBySpringCGLIB$$3c109cf5:CGLIB 生成的MyServiceImpl子类,类名遵循"目标类 +$$EnhancerBySpringCGLIB$$+ 哈希值"的命名规则(SpringNamingPolicy的产物)。

需要留意的是,$$后的哈希片段(如3c109cf5)与类加载环境相关,实际运行时可能不同,这是正常现象。

5.3 运行方式

该模块是 Maven 多模块工程spring-reading下spring-aop聚合模块的子模块(见 spring-aop/pom.xml 中声明的<module>spring-aop-aopProxyFactory</module>),依赖 Spring AOP 相关 jar。可直接在 IDE 中运行AopProxyFactoryDemo的main方法,或通过 Maven 编译后执行。

六、向上看:ProxyFactory——更便捷的编程式入口

在真实开发中,我们很少直接使用AopProxyFactory,而是使用它的上层封装ProxyFactory。仓库 spring-aop-proxyFactory 模块对此有完整介绍:ProxyFactory继承自ProxyCreatorSupport,内部持有一个AopProxyFactory(默认即DefaultAopProxyFactory),getProxy()方法最终就是调用createAopProxy(this).getProxy()完成代理创建。

其演示代码非常简洁:

public class ProxyFactoryDemo { public static void main(String[] args) { // 创建代理工厂&创建目标对象 ProxyFactory proxyFactory = new ProxyFactory(new MyService()); // 获取代理对象 Object proxy = proxyFactory.getProxy(); // 调用代理对象的方法 System.out.println("proxy = " + proxy.getClass()); } }

理解AopProxyFactory之后,再看ProxyFactory乃至 Spring 容器中的ProxyFactoryBean、AutoProxyCreator(如 AnnotationAwareAspectJAutoProxyCreator),会发现它们最终都汇聚到同一个决策入口——这正是"工厂接口 + 默认实现"设计的价值:代理创建策略被收敛为单一接口,上层无论通过何种方式装配 Advisor,最终都由DefaultAopProxyFactory依据配置统一裁决代理方式。

七、小结

本文围绕AopProxyFactory完成了从接口契约到实现原理的完整梳理:

  • 接口层面:createAopProxy(AdvisedSupport config)是唯一入口,返回AopProxy,并对代理对象提出"实现所有被代理接口、实现 Advised、实现 equals、可序列化、线程安全"等契约要求;
  • 实现层面:DefaultAopProxyFactory依据optimize/proxyTargetClass/ 是否提供用户接口三个条件决策 CGLIB,再对"目标是接口或已是代理类"做 JDK 兜底,配置不合法时抛出AopConfigException;
  • 实践层面:通过 AopProxyFactoryDemo.java 可直观对比$Proxy0与EnhancerBySpringCGLIB两种代理类。

如果想继续深入,推荐按以下顺序阅读仓库内相关模块:AopProxy 源码分析(两种代理的getProxy与拦截实现)→ ProxyFactory(编程式入口)→ AdvisorChainFactory 源码分析(拦截器链的构建),即可打通"配置 → 选型 → 生成 → 拦截执行"的完整 AOP 代理链路。

  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载
上一篇:十分钟恢复 Windows 11 快速启动工具栏:ExplorerPatcher 完整上手指南
下一篇:esp-iot-solution 中的 BLE UART JSONL 信封协议(v1)全解析:基于 ESP-VoCat 的 OpenCode 权限审批与会话同步协议

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询