JDK动态代理必须基于接口,因其通过生成实现相同接口的代理类来工作,与目标类是兄弟关系;无接口时Spring AOP退化为CGLIB代理,但受限于final类、private/static/final方法等。

Java 中接口在 AOP 动态代理中起着决定性作用——JDK 动态代理必须基于接口,这是它与 CGLIB 的根本区别。
为什么必须有接口
JDK 动态代理本质是“生成一个新类”,这个类实现目标对象所声明的所有接口,但不继承目标类。它无法修改类结构,只能靠接口契约来统一方法签名和调用约定。没有接口,JDK 就找不到可实现的契约,代理创建直接失败。
代理对象与目标对象的关系
代理对象和目标对象是“兄弟关系”,不是父子关系:
- 它们都实现同一个接口,对外表现一致
- 代理对象内部持有目标对象引用(通常通过 InvocationHandler 构造传入)
- 所有方法调用最终都经由 invoke() 方法转发给目标对象,中间可插入日志、事务等逻辑
接口缺失时的后果
如果目标类没实现任何接口,Spring AOP 默认会退回到 CGLIB 代理。但要注意:
立即学习“Java免费学习笔记(深入)”;
- CGLIB 通过字节码技术生成目标类的子类,要求目标类不能是 final
- 目标类的构造函数不能有复杂依赖或副作用(子类构造时会调用父类构造器)
- private 方法、static 方法、final 方法无法被 CGLIB 拦截
如何确保接口能被正确代理
实际开发中需注意三点:
- 业务类尽量面向接口编程,定义清晰的 Service 接口并让实现类实现它
- Spring 配置中避免强制使用 CGLIB(如
proxy-target-class="true"),除非明确需要 - AOP 切点表达式应匹配接口方法签名,例如
execution(* com.example.service.UserService.*(..)),而不是实现类全限定名



















