周五早上,闹钟还没响,人先醒了。
不是被吵醒的。是心里那根弦自己松了------睁眼就知道,今天上完,能歇两天。
周五这天,上班族的心情是有固定形状的。有人在群里发"今天周五",有人已经开始盘算晚上吃点什么。小王也高兴,但他高兴得有点早------今早没车骑,还得出门。
昨晚那版上线,收工很晚。出公司的时候他没去地铁站,直接打了辆车回家。电动车就那么停在地铁站旁边,锁都没动过。
所以今天早上没车骑。他得比平时早一点出门,打车去公司。

昨天一辆车送他回家,今天一辆车送他去上班
昨晚一趟,今早一趟。两趟车打下来,他琢磨出点意思:这一路,他其实没跟司机直接说上几句话。多少钱、走哪条路、车牌对不对,全是平台在管。
他叫的从来不是"一辆车",是平台。
今天聊的就是这个站在中间的"平台":代理(Proxy)------你和一个真正干活的对象之间,隔着一个替它接话的人。系列第七篇。
01 你压根没跟司机说上话
昨晚叫车,手机点两下,屏幕上跳出"预计 5 分钟"。
车到了,车牌对得上。上车,下车,付款。他没跟司机谈价,也没存司机的手机号------行程一结束,那个号码很快就不能拨了。
当时只觉得方便。今天再想,这里头有三件事一直在替他兜着:车牌得对上、号码不能外露、这一单得留痕。
如果代码里也这么直白地找个"真司机":
java
// 直接叫真司机
Ride driver = new RealDriver("王师傅");
driver.go("家");
三行就能跑,规矩却全在调用的人脑子里
问题是这三件事没人管:谁都能 new 一个司机出来,绕过所有规矩;想统一加个录音、加个计价,得在每个调用点抄一遍;哪天要换个人开车,得把所有 new 的地方翻出来改。
直接调用最省事,也最没规矩。
生活里也一样。路边招手拦车,车能来,路能走。但绕不绕路、多少钱、发票怎么开、出了事找谁,全凭运气。
02 中间站一个平台
代理的写法,一句话:平台和司机实现同一个接口,平台手里握着司机。
java
// 一个接口:把一段路走完
interface Ride { void go(String to); }
// 真司机:手握方向盘的那位
class RealDriver implements Ride {
private final String name;
RealDriver(String name) { this.name = name; }
public void go(String to) {
System.out.println(name + " 开车去 " + to);
}
}
// 平台:你只跟它说话
class RidePlatform implements Ride {
private final Ride driver;
RidePlatform(Ride driver) { this.driver = driver; }
public void go(String to) {
String orderId = createOrder(to); // 下单,先留痕
openVirtualNumber(); // 虚拟号,真号不外露
driver.go(to); // 开车的还是司机
settle(orderId); // 结算、录音归档
}
}
你调平台,平台转头才去叫司机
调用只有两行:
java
Ride ride = new RidePlatform(new RealDriver("王师傅"));
ride.go("家");
调用方眼里只有平台,司机藏在后面
重点不在"多套了一个类"。重点是:你按的是平台的按钮,平台转头才去叫司机。 中间那一段,就是代理的全部。

下单在平台,开车在司机,两边互不打听
司机知不知道有平台?知道。但他不关心是谁在叫他。平台也不关心路怎么绕。两边只通过 Ride 这一个接口说话。
以后加行程分享、加投诉入口,都在平台里加,司机那几行一个字不用动。
03 平台不止管一件事
平台做的事,拆开看是三件。
挡人。 你看到的司机电话是虚拟号,司机看到的你也一样。号码只用在这一单里,行程一结束就失效。这叫保护代理------它先替你把关,够格了才放行。
占位。 车还在几公里外,App 先给你车牌和预计到达时间。真的车没到,先给个东西占着位。这叫虚拟代理------代码里常见的样子是:真对象开销大,用到才创建。
记住了。 常用地址、上次走的哪条路、这单多少钱,不用每次重来。这叫缓存代理。
写在代码里,前两种长这样:
java
// 挡人:先验资格,号码走虚拟的
class Protector implements Ride {
private final Ride driver;
Protector(Ride driver) { this.driver = driver; }
public void go(String to) {
if (!driverReady()) throw new IllegalStateException("司机还没到位");
System.out.println("拨号走 " + virtualNumber());
driver.go(to);
}
}
// 占位:真司机用到才建,先不急着造
class Placeholder implements Ride {
private Ride real;
public void go(String to) {
if (real == null) real = new RealDriver("王师傅");
real.go(to);
}
}
一个管放不放行,一个管什么时候真去建
三种名字不一样,做的事是同一件:在"你"和"真东西"之间,管住那道门。
这里有一处对不上,得挑明。打车平台是现实里的一家真公司,代理只是代码里的一个对象,跟真对象住在同一个进程里。平台会抽成、会调度几十上百辆车;代码里的代理,大多只守着它代理的那一个真对象。
平台比代理能干得多。真正对得上的,是"中间那一段":你想办事,得先经过它。
04 跟装饰器分清楚:一个加料,一个把门
第五篇给电动车加挡风被,今天打车。两件事看着都像"多了一层",其实差得远。
装饰器:车还是你骑,方向盘在你手里。挡风被包在外面,开春能拆,也能再套个把套。它给本体加能力。
代理:方向盘不在你手里。你坐后座,司机在开。你甚至不需要知道他是谁。它给访问加一道关卡。
java
// 装饰器:本体外面加一层,还是你自己骑
Commute scooter = new WindshieldCover(new ElectricScooter());
// 代理:你只认识平台,方向盘在别人手里
Ride ride = new RidePlatform(new RealDriver("王师傅"));
包法一样,谁握方向盘不一样

左边方向盘在自己手里,右边已经交出去了
代码上的差别还能再细一层。
- 装饰器一般由调用方自由叠,叠几层、什么顺序,组装的地方说了算;
- 代理一般由框架定死,你调的就是代理,很多时候根本不知道后面还有没有真对象;
- 装饰器基本原样转发;代理可以不转发------保护代理直接拒绝,虚拟代理先不创建真对象。
装饰器怕你不够用,代理怕你乱用。
05 JDK 里最正宗的代理
JDK 自己就带了一个:java.lang.reflect.Proxy。照着一个接口,它在运行时现场给你造一个代理对象,那个类你一个字都不用写。
java
Ride real = new RealDriver("王师傅");
Ride platform = (Ride) Proxy.newProxyInstance(
Ride.class.getClassLoader(),
new Class<?>[]{Ride.class},
(p, method, args) -> {
System.out.println("平台记一笔:" + method.getName());
return method.invoke(real, args); // 最后动手的还是司机
});
platform.go("家");
每次调用先被 lambda 接住,再转给真司机
中间那个 lambda 就是平台。调用全被它接住,记一笔,再转给真司机。连平台那个类都省了,JDK 现场替你造。
JDK 之外,形状也一样。RMI 里那个存根(stub),你以为在调本地方法,实际是通过网络发给了另一台机器,这是远程代理;Hibernate 查出来的关联对象,第一次访问才去查库,这是虚拟代理;MyBatis 的 Mapper 只写一个接口,实现类就是 Proxy 现场生成的;Spring 的事务和日志,套在 Service 外面,业务代码里看不见代理的影子。
壳是 JDK 给的,门里的规矩还得自己写。
06 什么时候,别乱叫代理
代理不是所有调用都该套。
自己家门口挪个车,犯不上叫平台。一次性的、不碰外部资源的、没人需要管着的调用,直接调就行。套上代理,本来一行能看懂的代码,得跳两层才知道谁在干活。
代理层也别塞业务。代理只管门,不管屋里的事。真把业务逻辑写进去,它就变成了第二个真对象,以后改需求得改两处,还容易改漏一处。
还有排查。调用链上多一个代理,报错栈就多一层。线上出问题,第一件事是先弄清这行到底是谁在执行。
这两年写代码,AI 套一层代理很快------注解一加,AOP 一配,看上去就完事了。
但它不会告诉你:这道门该不该有,谁该被挡在外面。
代理能替你把门,门朝哪开还得你自己定。
07 收工
周五下班,天还亮着。
他坐地铁到那一站,出站,走到停车的地方。电动车还在,车筐里落了点灰。钥匙一拧,车灯亮了一下------停了一天一夜,还有电。
骑回去的路上,风从耳边过。昨天打车,今早打车,绕了一圈,都是为了把这辆车接回自己手里。
这一路他也算把"代理"看明白了:真正干活的那位一直都在,只是你和它中间,多了一道门。
跟前面六篇对一句:单例管造几个,工厂管怎么造,模板方法管事情怎么走,策略管这一步用什么做法,装饰器管外面加几层,命令管把要做的事做成对象,代理管谁能碰到真东西、怎么碰。
带走一句话:代理 = 你和真正干活的那个之间,多一道门。
门可以一直开着,但让谁进,它说了算。
参考资料
- 📕 《设计模式:可复用面向对象软件的基础》(GoF 原著)------代理模式的原始定义:为其他对象提供一个替身,以控制对这个对象的访问
- 📗 《Head First 设计模式》第 11 章------远程代理、虚拟代理、保护代理,本文"打车"的近亲
- 📘 Java 官方文档 · Proxy(java.lang.reflect)------动态代理,框架们都在用的那套
- 📙 Spring 官方文档 · AOP------事务和日志是怎么套在 Service 外面的