Java学习:动态代理的一点小理解
手动实现#
之前的一篇讲IoC的博文提到了代理模式,事实上代理模式就是AOP实现的重要基石。但是上面的代码有一个显而易见的缺陷,也就是之前讲解反射内容时提到的:不具备动态性。
上面代码中的Server就像是反射理解博文中提到的工厂订单管理员一样,每增加一样菜系,就需要相应更新手上的菜单。类比反射特性,我们完全可以做到让服务员不需要手上拿着菜单来为顾客服务:
// 服务员实现类
public class ServerImpl implements Server {
// 采用组合的方式引入noodles
private Noodles noodles;
@Override
public void getNoodles() {
// 代理模式
noodles.getNoodles();
}
// 利用反射特性
@Override
public void setNoodles(String noodles) {
this.noodles = (Noodles) Class.forName(noodles).newInstance();
}
}
}
API实现#
之前的动态代理是我们手动实现的,JDK其实自带了很多实现动态代理的类和方法。可能初次接触时会显得有些复杂,所以先画一张图来表示我们整个的设计思路:

之前在讨论IoC举的餐馆用例中,除了不具备动态性之外还有一个缺陷:服务员只服务于本餐馆,也就是说,我们能不能把服务员这个群体抽离出来,不仅服务这个餐馆,也能服务于多家餐馆,以实现代码的复用呢?
现实生活中有一种类型的公司,叫做家政公司,我们可以类比来编写一个服务员工厂:
public class ServerFactory implements InvocationHandler {
private Object target;
// 设定服务对象
public void setTarget(Object target) {
this.target = target;
}
// 返回单个服务员
public Object getServer() {
return Proxy.newProxyInstance(
this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
// 用于实现代理的重写方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
这是利用java.lang.reflect包提供的动态代理相关类和方法编写的ServerFactory。最后的invoke方法是继承接口要求的重写方法,不妨碍我们理解,暂时忽略,主要关注前半部分的代码。
前半部分代码非常好理解:
- 工厂接到业务需求:给某某提供服务。派出服务团队
- 利用setTarget确定服务对象。
- 使用getServer返回对应的服务人员。
下面使用这个类进行测试,同样以与餐馆厨师对接为例:
public class ServerTest {
public static void main(String[] args) {
// 获取服务员团队
ServerFactory serverGroup = new ServerFactory();
// 设定该团队对接面食厨师
serverGroup.setCooker(new NoodleCooker());
// 获取单个服务员
Cooker server = (Cooker) serverGroup.getServer();
// 顾客点餐
server.cooking();
}
}
// NoodleCooker类
public class NoodleCooker implements Cooker {
@Override
public void cooking() {
System.out.println("开始下面了!");
}
}
// Cooker接口
public interface Cooker {
void cooking();
}
我们实现了服务员和餐馆之间的解耦。有了服务员工厂,可以给任意的客户提供专业服务团队(setTarget),并且为每一次服务需求提供单个服务员进行实现(getServer)。
Proxy的实现#
探讨一下java中Proxy实现的大概机理,主要是针对invoke方法的理解。
根据反射的相关知识我们知道,invoke方法是Method类的方法,而Method类的对象是包含在class对象内部的,代表类的方法信息。invoke方法本质上是驱动某一个实例来调用method:
public class Person{
public int eat(String name){
System.out.println("eating...");
return 1;
}
}
// method是eat方法的实例
Person jojo = new Person();
Method method = jojo.getClass().getMethod("eat");
int result = method.invoke(jojo,"Jonathan");
// 等价于
int result = jojo.eat("Jonathan");
由此大概可以想到Proxy是怎么实现代理的了:
- 开始执行server.cooking()
- 读取NoodleCooker.class中cooking的method实例
- 将method实例和server.cooking()的参数传入ServerFactory.invoke()
- 执行invoke内部的代码
- server.cooking执行完毕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话