你好,我是风一样的树懒,一个工作十多年的后端专家,曾就职京东、阿里等多家互联网头部企业。公众号“吴计可师”,已经更新了近百篇高质量的面试相关文章,喜欢的朋友欢迎关注点赞
代理模式详解
一、定义与核心思想
代理模式(Proxy Pattern)是一种结构型设计模式,通过创建代理对象控制对原始对象的访问。代理充当客户端与真实对象之间的中介,用于延迟处理、访问控制、功能增强等场景,核心在于间接访问与职责分离。
二、适用场景
- 远程代理:访问远程对象(如RPC调用)。
- 虚拟代理:延迟加载大资源(如图片懒加载)。
- 保护代理:控制权限(如接口访问控制)。
- 缓存代理:缓存请求结果,提升性能。
- 日志/监控代理:记录方法调用信息。
三、模式结构
| 角色 | 职责 |
|---|---|
| 抽象主题(Subject) | 定义真实对象与代理的共用接口(如UserService)。 |
| 真实主题(Real Subject) | 实现核心业务逻辑(如UserServiceImpl)。 |
| 代理(Proxy) | 持有真实对象的引用,控制访问并可选增强功能(如UserServiceProxy)。 |
四、实现方式
1. 静态代理(显式代理)
// 抽象主题
public interface UserService {
void saveUser(User user);
}
// 真实主题
public class UserServiceImpl implements UserService {
@Override
public void saveUser(User user) {
System.out.println("保存用户:" + user.getName());
}
}
// 代理类
public class UserServiceProxy implements UserService {
private UserService realService;
public UserServiceProxy() {
this.realService = new UserServiceImpl();
}
@Override
public void saveUser(User user) {
checkPermission(); // 增强功能:权限校验
realService.saveUser(user);
logOperation(); // 增强功能:日志记录
}
private void checkPermission() {
System.out.println("权限校验通过");
}
private void logOperation() {
System.out.println("操作日志已记录");
}
}
// 使用
UserService service = new UserServiceProxy();
service.saveUser(new User("Alice"));
2. 动态代理(运行时生成代理类)
-
JDK动态代理(基于接口):
public class LoggingHandler implements InvocationHandler { private Object target; public LoggingHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用方法:" + method.getName()); return method.invoke(target, args); } } // 创建代理 UserService realService = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, new LoggingHandler(realService) ); proxy.saveUser(new User("Bob")); -
CGLIB代理(基于类继承):
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { System.out.println("CGLIB代理前置处理"); return proxy.invokeSuper(obj, args); }); UserService proxy = (UserService) enhancer.create(); proxy.saveUser(new User("Charlie"));
五、核心优势
- 职责清晰:代理类专注非业务逻辑(如安全、日志),真实类专注核心功能。
- 扩展灵活:动态代理无需为每个类显式创建代理。
- 保护真实对象:隐藏敏感操作,控制访问路径。
六、应用案例
- Spring AOP:通过动态代理实现事务管理、日志切面。
- MyBatis Mapper:接口方法调用代理生成SQL执行。
- RPC框架:客户端存根(Stub)代理远程服务调用。
- 图片懒加载:虚拟代理延迟加载大图直至需显示时。
七、代理模式 vs 装饰器模式
| 维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问,增强非功能性逻辑(权限/日志) | 增强对象功能,添加新职责 |
| 创建时机 | 通常由客户端决定是否使用代理 | 通常由客户端动态组合装饰器 |
| 关系 | 代理与目标对象生命周期可能独立 | 装饰器与组件生命周期一致 |
八、进阶应用
1. 延迟初始化(虚拟代理)
public class ImageProxy implements Image {
private String path;
private RealImage realImage;
public ImageProxy(String path) {
this.path = path;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(path); // 实际使用时加载
}
realImage.display();
}
}
2. 分布式服务代理(远程代理)
// 客户端调用远程服务
public class RemoteServiceProxy implements OrderService {
@Override
public Order getOrder(String id) {
// 网络通信逻辑(如HTTP/RPC)
return remoteClient.fetchOrder(id);
}
}
3. 访问控制(保护代理)
public class ProtectedServiceProxy implements AdminService {
private AdminService realService;
private User currentUser;
public ProtectedServiceProxy(User user) {
this.realService = new AdminServiceImpl();
this.currentUser = user;
}
@Override
public void deleteData() {
if (!currentUser.isAdmin()) {
throw new SecurityException("权限不足");
}
realService.deleteData();
}
}
九、注意事项
- 性能开销:动态代理生成类可能影响启动速度,频繁调用需优化。
- 过度使用:简单场景无需引入代理,避免增加复杂度。
- 接口限制:JDK动态代理要求目标实现接口,CGLIB不能代理final类/方法。
十、总结
代理模式通过间接访问机制,在不侵入原始代码的前提下,为系统扩展提供强大灵活性。合理运用静态/动态代理技术,可有效解耦核心逻辑与辅助功能,是构建高维护性、高扩展性架构的关键工具。
今天文章就分享到这儿,喜欢的朋友可以关注我的公众号,回复“进群”,可进免费技术交流群。博主不定时回复大家的问题。 公众号:吴计可师