jdk动态代理基于接口和反射,通过proxy.newproxyinstance生成实现接口的代理类,调用统一委托给invocationhandler;cglib则通过继承目标类并覆写非final方法实现代理,可代理无接口类。

Java 反射生成动态代理类,核心是理解 java.lang.reflect.Proxy 和 InvocationHandler 的协作机制,面试时重点考察你是否真正掌握“代理对象如何被创建”“方法调用如何被拦截”“为什么必须基于接口”等底层逻辑,而不是只会背代码。
动态代理的两种实现方式必须分清
Java 中动态代理只有两种官方支持的方式:
-
JDK 动态代理:基于接口,通过
Proxy.newProxyInstance()创建代理实例,底层生成字节码($Proxy0类),要求目标类至少实现一个接口; -
CGLIB 动态代理:基于继承,通过子类重写方法实现代理,可代理无接口的类,但不能代理
final类或final方法。
面试中若被问“能不能代理没有接口的类”,答“JDK 不行,CGLIB 可以”还不够,要补一句:“因为 JDK 代理本质是实现接口,而 CGLIB 是继承目标类并覆写非 final 方法。”
手写 Proxy + InvocationHandler 的关键步骤
写出能跑通的最小可运行示例,比死记硬背更重要。注意三要素缺一不可:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义一个接口(如
UserService)和其实现类(如UserServiceImpl); - 实现
InvocationHandler,重写invoke(),在其中添加前置/后置逻辑(如日志、权限校验); - 调用
Proxy.newProxyInstance(类加载器, 接口数组, handler)获取代理对象,再强转为接口类型调用。
特别提醒:传入的 ClassLoader 建议用 target.getClass().getClassLoader(),避免因类加载器不同导致 ClassCastException。
面试常挖的几个深水点
光会写不够,面试官常追问这些细节:
-
生成的代理类在哪?能不能看到源码?:默认不落地,但可通过
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")输出.class文件,反编译后可见它实现了接口、持有InvocationHandler引用、所有方法都转发给handler.invoke(); - 为什么只能代理接口,不能代理具体类?:JDK 代理靠“实现接口”来统一方法签名,代理类需同时满足接口契约和反射调用要求,而类继承关系无法在运行时动态构造出满足多接口的子类;
- invoke() 方法里的 proxy 参数是干啥的?:就是当前生成的代理对象本身,可用于递归调用(慎用,防栈溢出)或判断代理身份。
和 Spring AOP 的关系要拎得清
Spring 默认优先用 JDK 代理(有接口时),没接口才退化到 CGLIB。这不是 Spring 自己造的轮子,而是对这两种机制的封装和增强:
- 你写的
@Aspect切面,最终会被 Spring 转成Advice,包装进InvocationHandler或 CGLIB 的MethodInterceptor; - Spring 的
Advised接口允许运行时增删 Advisor,这是原始 Proxy 所不具备的能力; - 面试提到 Spring,别只说“自动代理”,要点出它如何桥接反射代理与 IoC 容器(如通过
BeanPostProcessor在 bean 初始化后织入代理)。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










