动态代理的实现原理
动态代理就是程序在运行期间,动态地创建一个“中介”对象,这个中介对象可以代理另一个对象,这种特性也是实现 Spring AOP 的基础。
既然有“动态”代理,当然也有“静态”代理,静态代理就是代码还没跑之前,就先把“经纪人”写死了。
动态代理是程序跑起来之后,Java 利用反射机制,临时在内存里创建出一个“经纪人”对象,这个对象可以代理周杰伦,拦截对周杰伦的访问,然后做一些附加操作,(比如收钱,签合同等),然后再让周杰伦唱歌。
1. 类比:明星和经纪人
- 原对象:周杰伦 -> 只负责唱歌,其他的不管
- 代理对象:经纪人 -> 解决周杰伦的运营问题,比如代言、演唱会等
- 客户端:粉丝 -> 只需要找到经纪人,就能找到周杰伦
理解动态代理(Dynamic Proxy)几乎是理解 Spring 核心原理(AOP, 事务管理)的钥匙。
简单来说,动态代理就是在程序运行期间,动态地创建一个“中介”对象,代替原对象做事。
2. JDK 动态代理核心代码
JDK 的动态代理主要依赖 java.lang.reflect.Proxy 类和 InvocationHandler 接口。
关键规则: JDK 动态代理要求被代理的对象必须实现一个接口。
第一步:定义接口 (Singer) 和实现类 (JayChou)
- 接口
// 接口:定义功能
public interface Singer {
String sing();
}
- 实现类
// 实现类:真正的周杰伦
class JayChou implements Singer {
@Override
public String sing() {
System.out.println("周杰伦:快使用双截棍,哼哼哈兮~");
return "谢谢";
}
}
第二步:编写动态代理逻辑(经纪人的工作手册)
先要实现 InvocationHandler,这里面定义了“中介”要干什么。
- 代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Agent {
// 1. 创建被代理的对象(真正的周杰伦)
static Singer realSinger = new JayChou();
// 2. 动态生成代理对象(经纪人)
public static Singer agent = (Singer) Proxy.newProxyInstance(
realSinger.getClass().getClassLoader(), // 类加载器
realSinger.getClass().getInterfaces(), // 实现了哪些接口
new InvocationHandler() { // 处理器(核心逻辑)
/*
* public Object invoke(Object proxy, Method method, Object[] args) 此方法签名的注解:
* @param proxy -- 这是代理对象本身,也是最危险的参数
* @param method -- 此参数的作用是告诉外界调用的方法,比如是想让周杰伦 sing() 还是 dance()
* @param args -- 方法参数,比如传递进来的是 agent.sing("七里香"),那么 args 数组里装的就是 ["七里香"]
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// --- 方法执行前的增强逻辑 ---
System.out.println("经纪人:先谈出场费,安排行程...");
// --- 真正的方法执行 ---
// 利用反射,调用 realSinger 的方法
Object result = method.invoke(realSinger, args);
// --- 方法执行后的增强逻辑 ---
System.out.println("经纪人:演出结束,结算尾款。");
return result;
}
}
);
}
- 启动类
public class ProxyDemo {
public static void main(String[] args) {
// 3. 调用代理对象的方法
String sing = Agent.agent.sing();
// sing 有一个返回的值,这里接收了 JayChou 返回过来的 “谢谢”
System.out.println(sing);
}
}
运行结果:

3. 动态代理的用处
这就是动态代理的强大之处——解耦(Decoupling)。
- 无侵入式修改: 如果有 100 个类(Singer, Dancer, Actor…),都需要在做事之前打印日志。如果去改这 100 个类,代码会乱成一锅粥。用动态代理,只需要写一个 Handler,就能通吃。
- 复用性: 那个
InvocationHandler可以应用在任何对象上,只要逻辑是通用的(比如计算方法执行时间)。 - 灵活性: 可以在任何时间点,动态地添加增强逻辑。
4. 与 Spring 的关系
在 Spring Boot 里经常用的功能,底层全都是动态代理:
@Transactional事务控制: 当调用一个加了@Transactional的方法时,Spring 并没有直接调用代码。- Spring 生成了一个动态代理。
- 代理对象在调用方法前,执行
conn.setAutoCommit(false)(开启事务)。 - 运行代码。
- 如果没有异常,代理对象执行
conn.commit()。 - 如果有异常,代理对象执行
conn.rollback()。 - 完全感知不到这个过程,这就是 AOP(面向切面编程)。
MyBatis 的 Mapper 接口: 写
UserMapper接口时,从来没有写过它的实现类,为什么能直接@Autowired注入使用?- 因为 MyBatis 在运行时生成了一个动态代理对象,它拦截了方法调用,把方法名变成了 SQL 语句去数据库跑了一遍。
5. CGLIB (Code Generation Library)
JDK 动态代理有一个缺陷:目标对象必须实现接口。 如果的类没有实现任何接口(只是一个普通的类),JDK 代理就废了。
这时候 Spring 会自动切换到 CGLIB 动态代理。
- 原理: CGLIB 不通过接口,而是通过继承。它在内存里动态生成一个被代理类的子类,并重写父类的方法来实现增强。
- 限制: 因为是基于继承,所以被代理的类或方法不能是
final的(因为final不能被继承或重写)。
总结: 在现代 Spring Boot (2.x 以后) 中,只要引入了 AOP 依赖,Spring 默认倾向于使用 CGLIB,因为它性能越来越好,且不再强制要求接口。