动态代理实现原理分析
字数 1746 2025-08-23 18:31:08
动态代理实现原理详解
一、代理模式概述
代理是一种常用的设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息、过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
类比:可以理解为朋友圈中的代购,在买家与卖家间进行协调。
二、静态代理
1. 静态代理实现方式
要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样方法。
实例:电影公司委托影院播放电影,影院可以在播放前后添加自己的业务逻辑(如卖爆米花、饮料等)。
2. 静态代理代码实现
// 定义接口
public interface Movie {
public void show();
}
// 委托类
class Real implements Movie {
public void show() {
System.out.println("您正在观看电影");
}
}
// 代理类
public class StaticPoxy implements Movie {
private Movie movie;
public StaticPoxy(Movie movie) {
this.movie = movie;
}
@Override
public void show() {
System.out.println("电影马上开始了,爆米花、饮料快来买啊");
movie.show();
System.out.println("电影已经结束了,爆米花、饮料买回家吧");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
Movie test = new Real();
System.out.println("-----无代理-----");
test.show();
System.out.println("-----静态代理-----");
StaticPoxy staticPoxy = new StaticPoxy(test);
staticPoxy.show();
}
}
3. 静态代理优缺点
优点:
- 可以在不修改委托类源代码的情况下,通过代理类扩展功能
缺点:
- 代理类和委托类都实现相同接口,导致代码重复
- 添加新接口时,代理类和委托类都需要修改
- 产生冗余代码,增加运维成本
三、动态代理
1. 动态代理概述
动态代理是指在内存中动态构建代理对象(需要指定要代理的目标对象实现的接口类型),利用JDK的API生成指定接口的对象,也称为JDK代理或接口代理。
主要涉及java.lang.reflect包下的:
Proxy类InvocationHandler接口
2. 核心类与接口
(1) Proxy类
Proxy类继承了java.io.Serializable接口,其中最重要的方法是:
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
)
参数说明:
loader:类加载器interfaces:要代理的接口数组h:InvocationHandler对象
(2) InvocationHandler接口
每个proxy代理实例都有一个关联的调用处理程序,当代理实例调用方法时,方法调用会被编码分派到调用处理程序的invoke方法。
public interface InvocationHandler {
public Object invoke(
Object proxy,
Method method,
Object[] args
) throws Throwable;
}
参数说明:
proxy:代理对象method:要调用的代理对象方法args:方法调用参数
3. 动态代理实现示例
(1) 定义接口和实现类
// 卖酒接口
public interface SellWine {
public void sell();
}
// 卖酒实现类
public class Wine implements SellWine {
public void sell() {
System.out.println("卖酒");
}
}
(2) 实现InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicPoxy implements InvocationHandler {
private Object agent;
public DynamicPoxy(Object poxy) {
this.agent = poxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("销售开始 代理商是:" + this.getClass().getSimpleName());
method.invoke(agent, args);
System.out.println("销售结束");
return null;
}
}
(3) 测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Wine wine = new Wine();
InvocationHandler invocationHandler = new DynamicPoxy(wine);
SellWine dynamicPoxy1 = (SellWine) Proxy.newProxyInstance(
wine.getClass().getClassLoader(),
wine.getClass().getInterfaces(),
invocationHandler
);
dynamicPoxy1.sell();
}
}
4. 动态代理原理分析
(1) 代理文件生成
在测试类中添加以下代码可生成代理类文件:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
代理类生成流程:
- 调用
Proxy.newProxyInstance() - 进入
getProxyClass0()方法 - 执行
proxyClassCache.get(loader, interfaces) - 在第二轮循环中调用
get()方法 - 调用
ProxyClassFactory的apply()方法 - 通过
defineClass0()生成class对象
ProxyClassFactory的作用:
- 生成新的代理类的class对象
- 是
BiFunction接口的实现类 - 在
apply()方法中:- 检查传入的class对象数组
- 生成新的代理类的类名
proxyName - 调用
defineClass0方法生成代理类class对象
(2) 方法调用流程
- 调用
dynamicPoxy1.sell()时,会调用$Proxy0.class中的sell() $Proxy0.sell()中会调用h.invoke()h是在测试类中创建的DynamicPoxy对象- 因此实际调用的是
DynamicPoxy.invoke() - 通过
method.invoke(agent, args)调用原始对象的sell()方法
5. 动态代理扩展性
动态代理的优势在于可以轻松扩展新功能而无需修改代理类。
示例:添加卖烟功能
// 卖烟接口
public interface SellCigarette {
public void sell();
}
// 卖烟实现类
public class Cigarette implements SellCigarette {
public void sell() {
System.out.println("卖烟");
}
}
// 测试类扩展
public class Test {
public static void main(String[] args) {
Wine wine = new Wine();
Cigarette cigarette = new Cigarette();
InvocationHandler invocationHandler1 = new DynamicPoxy(wine);
InvocationHandler invocationHandler2 = new DynamicPoxy(cigarette);
SellWine dynamicPoxy1 = (SellWine) Proxy.newProxyInstance(
wine.getClass().getClassLoader(),
wine.getClass().getInterfaces(),
invocationHandler1
);
SellCigarette dynamicPoxy2 = (SellCigarette) Proxy.newProxyInstance(
cigarette.getClass().getClassLoader(),
cigarette.getClass().getInterfaces(),
invocationHandler2
);
dynamicPoxy1.sell();
System.out.println("----------------");
dynamicPoxy2.sell();
}
}
四、总结对比
| 特性 | 静态代理 | 动态代理 |
|---|---|---|
| 实现方式 | 手动编写代理类 | 运行时动态生成 |
| 代码冗余 | 高(需实现相同接口) | 低(通用InvocationHandler) |
| 扩展性 | 差(新增接口需修改代码) | 好(只需新增接口和实现类) |
| 维护成本 | 高 | 低 |
| 性能 | 略高(直接调用) | 略低(反射调用) |
动态代理通过Java反射机制在运行时动态创建代理对象,避免了静态代理的代码冗余问题,大大提高了系统的灵活性和可扩展性。