在Java后端面试中,一个被高频提及的概念便是动态代理。很多初学者感觉它像“AI助手小孩”——听起来很强大,能自动帮我们处理复杂逻辑,但轮到自己实现或讲解原理时,却不知从何下手,只会用Spring AOP却不懂其底层。本文将通过“AI助手小孩”这个比喻,带你从痛点出发,彻底搞懂动态代理的核心概念、代码示例、底层逻辑及面试考点。
一、痛点切入:为什么需要动态代理?

假设我们有一个Child类,代表一个“小孩”,他需要完成写作业的任务。
public class Child {public void doHomework() { System.out.println("小孩正在写作业..."); } }
现在,我们想在写作业前后分别加上“思考”和“检查”的步骤。最直接的方式是修改原有代码,或使用静态代理:
public class ChildProxy { private Child child; public ChildProxy(Child child) { this.child = child; } public void doHomework() { System.out.println("1. 先思考"); child.doHomework(); System.out.println("2. 再检查"); } }
静态代理的缺点:
代码冗余:每个被增强的类(如Child、Teacher)都需要创建一个对应的代理类。
耦合高:代理类与目标类强绑定,接口变更时维护成本高。
扩展性差:无法灵活地在运行时切换增强逻辑。
这就像一个真实的“AI助手小孩”如果只能服务一个特定小孩,换个人就得重新定制,效率极低。由此,动态代理应运而生。
二、核心概念讲解:动态代理(Dynamic Proxy)
英文全称:Dynamic Proxy
中文释义:在程序运行时动态创建代理对象,对目标方法进行拦截和增强的技术。
拆解关键词:
动态:代理类不是在编译期生成,而是在JVM运行时通过反射等机制生成字节码并加载。
代理:充当中间角色,控制对真实对象的访问。
生活类比:想象一个“AI助手小孩”热线。你不需要知道背后是哪个具体小孩在服务,只需拨打统一号码,AI就会自动帮你接通、转接、记录。这个热线就是动态生成的“代理”,而真正做事的可能是不同的小孩(目标对象)。
核心价值:在不修改原类代码的前提下,实现功能的横向扩展(如日志、事务、权限校验),是Spring AOP的基石。
三、关联概念讲解:JDK Proxy vs. CGLIB
Java中实现动态代理主要有两种方式:
1. JDK动态代理
定义:基于接口的动态代理,要求目标类必须实现至少一个接口。
核心类:
java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。
2. CGLIB动态代理
定义:基于子类的动态代理,通过动态生成目标类的子类来创建代理对象,无需接口。
全称:Code Generation Library
关系与差异对比:
| 对比维度 | JDK Proxy | CGLIB |
|---|---|---|
| 实现基础 | 接口 | 子类(继承) |
| 目标类要求 | 必须实现接口 | 无接口要求,但不能是final类 |
| 性能 | 早期反射调用较慢,Java8+已优化 | 生成字节码,调用略快 |
| 依赖 | JDK自带 | 需引入CGLIB包(Spring内部集成) |
| 使用场景 | 有接口的轻量场景 | 无接口或需要更高性能的场景 |
一句话概括:JDK Proxy是“AI助手小孩”通过标准工牌(接口)为你服务;CGLIB是“AI助手小孩”直接模仿你本人(继承)替你办事。
四、概念关系与区别总结
逻辑关系:动态代理是一种设计思想(运行时生成代理),而JDK Proxy和CGLIB是其两种具体落地实现。
避免混淆:不要认为“动态代理=JDK Proxy”。面试时常问“Spring AOP默认用哪种?”答案取决于目标类是否有接口。
记忆口诀:有接口用JDK,无接口或final用CGLIB。
五、代码示例:JDK动态代理实现“AI助手小孩”
以下代码演示如何使用JDK Proxy为Child类(实现Person接口)生成一个“AI助手小孩”。
// 1. 定义接口 public interface Person { void doHomework(); } // 2. 真实目标类(小孩) public class Child implements Person { @Override public void doHomework() { System.out.println("小孩正在写作业..."); } } // 3. 调用处理器(增强逻辑) import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class AiAssistantHandler implements InvocationHandler { private Object target; // 真实对象 public AiAssistantHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【AI助手小孩】开始前:先思考"); Object result = method.invoke(target, args); // 调用真实方法 System.out.println("【AI助手小孩】结束后:再检查"); return result; } } // 4. 客户端使用 public class Main { public static void main(String[] args) { Child realChild = new Child(); AiAssistantHandler handler = new AiAssistantHandler(realChild); // 动态生成代理对象 Person proxyChild = (Person) Proxy.newProxyInstance( realChild.getClass().getClassLoader(), realChild.getClass().getInterfaces(), handler ); proxyChild.doHomework(); } }
输出结果:
【AI助手小孩】开始前:先思考 小孩正在写作业... 【AI助手小孩】结束后:再检查
关键步骤标注:
Proxy.newProxyInstance:JVM在内存中动态创建一个实现了Person接口的新类。handler.invoke:所有对proxyChild的方法调用都会转发到这里,由我们定义增强逻辑。method.invoke(target, args):通过反射调用真实对象的方法。
六、底层原理与技术支撑
动态代理能“凭空”生成类,主要依赖以下底层机制:
反射(Reflection):在运行时获取类的接口、方法元数据,并动态调用方法。
字节码生成:JDK Proxy利用
sun.misc.ProxyGenerator生成代理类的字节码数组,再通过ClassLoader加载到内存。CGLIB则使用ASM库直接操作字节码,生成目标类的子类。设计模式:内部使用了代理模式和策略模式(InvocationHandler作为策略)。
一句话定位:动态代理是Java语言提供的一种元编程能力,让代码可以在运行时自我修改和扩展,而无需提前编译。
七、高频面试题与参考答案
1. 静态代理和动态代理的区别?
答:静态代理在编译期已生成代理类,需为每个目标类手动编写;动态代理在运行时动态生成,一次编写可复用。动态代理更灵活、无侵入,是AOP的基础。
2. Spring AOP默认使用哪种动态代理?
答:默认使用JDK动态代理。如果目标类没有实现任何接口,则自动切换到CGLIB。也可通过配置
proxy-target-class=true强制使用CGLIB。
3. JDK动态代理为什么必须基于接口?
答:因为生成的代理类会继承
Proxy类(Java单继承),因此只能通过实现接口来扩展目标行为。CGLIB则通过继承目标类,无此限制。
4. 动态代理在框架中的典型应用场景有哪些?
答:Spring AOP(事务、日志、缓存)、MyBatis Mapper接口实现、RPC框架中的远程调用伪装、声明式权限控制等。
5. 如何实现一个简单的动态代理?
答:三步。① 定义接口和目标类;② 实现
InvocationHandler,在invoke中编写增强逻辑;③ 使用Proxy.newProxyInstance生成代理对象。
八、结尾总结
回顾核心:动态代理是运行时生成代理的技术,解决了静态代理的冗余和耦合问题。JDK Proxy(基于接口)和CGLIB(基于子类)是其两大实现。
重点与易错点:
面试时务必说清“有接口用JDK,无接口用CGLIB”。
不要忘记
InvocationHandler的invoke方法必须调用method.invoke才能执行原逻辑。
下一篇预告:我们将深入Spring AOP的源码,看看动态代理是如何与IOC容器结合,实现声明式事务的。同时,我们还会探讨“AI助手小孩”在异步编程中的另一种形态——CompletableFuture。
一句话记住动态代理:像AI助手小孩一样,你只管提需求,它自动在后台为你包装、增强、调度。
