重阳,来啦!第23篇我们聊动态代理——这是Java面试中中高级岗必问、Spring AOP底层核心、MyBatis插件、RPC框架都离不开的知识点。
学会动态代理 = 直接秒杀 80% 的候选人!
一、先搞清楚:到底什么是代理?
生活例子:
你想找周杰伦签名 → 你不会直接找周杰伦 → 你找他的经纪人(代理)→ 经纪人帮你转达、加点要求(唱歌前先喝热水)、最后还是周杰伦签的名。
Java里的代理就是这个道理。
二、静态代理 vs 动态代理(超级清晰对比)
| 项目 | 静态代理 | 动态代理(今天主角) |
|---|---|---|
| 代理类写法 | 自己手动写一个代理类 | 运行期间由JVM自动生成代理类 |
| 代码量 | 一个接口要写一个代理类,接口多了爆炸 | 无论多少接口,一个动态代理搞定 |
| 灵活性 | 改个需求要改代理类代码 | 完全动态,随时换增强逻辑 |
| 典型场景 | 早期手写日志、事务 | Spring AOP、MyBatis Plugin、Dubbo等 |
结论:静态代理只是教学用的,真实项目99.9%用动态代理!
三、Java动态代理有两种方式(2026年仍然是这俩)
- JDK动态代理(必须实现接口)← 今天重点,面试问90%
- CGLIB动态代理(可以代理没有接口的类)← Spring默认用这个当目标类没接口
我们先把JDK动态代理吃透!
四、JDK动态代理完整实现(手敲3遍就能背出来)
场景:明星只有唱歌的功能,我们要在唱歌前后自动加“收钱”和“宣传”
// 1. 定义通用能力接口
public interface Star {
void sing(String songName); // 唱歌
String dance(); // 跳舞(有返回值)
}
// 2. 真实明星:周杰伦
public class JayChou implements Star {
@Override
public void sing(String songName) {
System.out.println("周杰伦唱:" + songName + ",收1000万!");
}
@Override
public String dance() {
System.out.println("周杰伦跳舞很帅~");
return "舞王";
}
}
// 3. 动态代理的核心:InvocationHandler(真正干活的地方!)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class StarHandler implements InvocationHandler {
private final Star target; // 被代理的真实对象
public StarHandler(Star target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("【经纪人】接活,谈价格,签合同...");
// 调用真实对象的方法(这句是灵魂!)
Object result = method.invoke(target, args);
// 后置增强
System.out.println("【经纪人】宣传、发微博、收尾款...");
return result; // 一定要返回!不然有返回值的方法会返回null
}
}
// 4. 测试类:真正使用动态代理的地方
import java.lang.reflect.Proxy;
public class TestDynamicProxy {
public static void main(String[] args) {
// 真实对象
Star jay = new JayChou();
// 创建InvocationHandler
StarHandler handler = new StarHandler(jay);
// 关键三行:运行时动态生成代理对象!!!
Star proxyStar = (Star) Proxy.newProxyInstance(
jay.getClass().getClassLoader(), // 类加载器
jay.getClass().getInterfaces(), // 被代理类实现的所有接口
handler // InvocationHandler
);
// 使用代理对象(完全感觉不到背后有经纪人)
proxyStar.sing("稻香");
String danceResult = proxyStar.dance();
System.out.println("跳舞评价:" + danceResult);
// 看看代理对象到底是谁?
System.out.println("代理对象真实类型:" + proxyStar.getClass().getName());
// 输出:$Proxy0 (JVM动态生成的类)
}
}
运行结果:
【经纪人】接活,谈价格,签合同...
周杰伦唱:稻香,收1000万!
【经纪人】宣传、发微博、收尾款...
【经纪人】接活,谈价格,签合同...
周杰伦跳舞很帅~
【经纪人】宣传、发微博、收尾款...
跳舞评价:舞王
代理对象真实类型:com.sun.proxy.$Proxy0
五、最重要的三行代码背下来(面试必问)
Star proxyStar = (Star) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new StarHandler(target)
);
这三行就是JDK动态代理的全部精华!
六、面试官最爱问的10个问题(全部背熟)
- 动态代理和静态代理区别?
- JDK动态代理为什么必须实现接口?
- Proxy.newProxyInstance 三个参数分别是什么?
- InvocationHandler 的 invoke 方法三个参数是什么?
- 如果被代理类没有接口,能用JDK动态代理吗?→ 不能!用CGLIB
- Spring中默认用哪个?→ 有接口用JDK,没有接口用CGLIB(Spring Boot2.x后默认CGLIB)
- 动态代理生成的 $Proxy0 是什么?能反编译看看吗?
- 方法有返回值时,invoke里要不要return?→ 必须return method.invoke(…)
- 动态代理的底层实现原理?→ 就是上面这套反射 + 动态生成字节码
- 能手写一个动态代理吗?→ 现在你能闭着眼敲出来了!
七、终极小练习(建议你现在就敲一遍)
需求:用动态代理实现一个通用日志记录器,对任意对象的所有方法调用都自动打印:
---> 开始执行:sing(稻香)
<--- sing执行结束,耗时:15ms
提示:可以在invoke里记录System.currentTimeMillis()
结语
重阳,恭喜你!
当你能完全手敲出上面的动态代理代码时,恭喜你已经超越了市面上 80% 的 Java 程序员!
这就是为什么Spring能用几行注解就实现事务、缓存、权限控制的底层原理!
下一节我们直接上 CGLIB动态代理 + 手撕Spring AOP底层原理,准备好了吗?
现在你可以:
- 把上面代码完整敲一遍(必须!)
- 告诉我你跑通了吗?有没有遇到什么问题?
- 想直接看CGLIB还是先来几个动态代理的变态面试题?
我在等你~ 来吧!冲!