代理模式
Proxy — 为对象提供一个替身,控制对该对象的访问
一句话理解
不直接访问目标对象,而是通过一个代理人来间接访问,代理人可以在访问前后做额外的事。
解决什么问题
- 想在访问对象前做权限检查、日志记录、缓存等操作
- 目标对象创建成本高,想延迟加载(用到时才真正创建)
- 想对远程对象的调用做本地封装(远程代理)
和装饰器的区别
| 代理模式 | 装饰器模式 | |
|---|---|---|
| 目的 | 控制访问 | 增强功能 |
| 关注 | 是否能访问、何时访问 | 给对象叠加新能力 |
| 典型 | 权限控制、懒加载、缓存 | 加牛奶、加糖 |
怎么写
静态代理
public interface UserService {
void save(String name);
}
// 真实对象
public class UserServiceImpl implements UserService {
public void save(String name) {
System.out.println("保存用户: " + name);
}
}
// 代理对象
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void save(String name) {
System.out.println("[日志] 开始保存...");
target.save(name); // 委托给真实对象
System.out.println("[日志] 保存完成");
}
}UserService service = new UserServiceProxy(new UserServiceImpl());
service.save("张三");
// [日志] 开始保存...
// 保存用户: 张三
// [日志] 保存完成动态代理(Java JDK)
不用手写代理类,运行时动态生成:
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(proxyObj, method, args) -> {
System.out.println("[日志] 调用: " + method.getName());
Object result = method.invoke(new UserServiceImpl(), args);
System.out.println("[日志] 完成: " + method.getName());
return result;
}
);
proxy.save("张三");前端中的代理
// ES6 Proxy —— 语言原生支持
const user = { name: '张三', age: 25 }
const proxy = new Proxy(user, {
get(target, prop) {
console.log(`读取 ${prop}`)
return target[prop]
},
set(target, prop, value) {
console.log(`设置 ${prop} = ${value}`)
target[prop] = value
return true
}
})
proxy.name // 读取 name → "张三"
proxy.age = 26 // 设置 age = 26Vue 3 的响应式系统就是基于
Proxy实现的。
使用场景
- Spring AOP —— 通过动态代理实现切面(日志、事务、权限)
- MyBatis Mapper —— Mapper 接口没有实现类,运行时动态代理生成
- Vue 3 响应式 ——
Proxy拦截数据读写,触发视图更新 - 懒加载 —— 图片懒加载、大对象延迟初始化