1. 代理模式的核心概念与应用场景
代理模式(Proxy Pattern)是结构型设计模式中最具实用价值的一种。它通过创建一个代理对象来控制对原始对象的访问,这种控制可以体现在访问权限、性能优化、远程调用等多个维度。在实际开发中,代理模式的应用远比教科书上的简单示例要丰富得多。
我第一次深入理解代理模式的价值,是在优化一个电商平台的商品详情页时。当时页面加载需要同时调用库存服务、价格服务和推荐服务,频繁的远程调用导致响应时间超过2秒。通过引入代理模式,我们将这些调用封装在代理层,实现了请求合并和缓存机制,最终将响应时间降低到400毫秒以内。这个案例让我意识到,代理模式绝不仅仅是简单的"中间人",而是系统架构中不可或缺的润滑剂。
代理模式的核心在于"控制"二字。它通过代理对象对原始对象的访问进行拦截和增强,这种设计带来了几个关键优势:
- 职责清晰:将核心业务逻辑与辅助功能(如安全控制、日志记录)分离
- 开闭原则:可以在不修改原始类的情况下增加功能
- 访问控制:代理可以决定何时以及如何创建和使用真实对象
在Java生态中,代理模式的应用随处可见。Spring框架的AOP实现、MyBatis的Mapper接口、Hibernate的延迟加载,这些我们日常使用的技术背后都有代理模式的身影。甚至Java动态代理API本身,就是代理模式的最佳实践案例。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 静态代理与动态代理的实现对比
2.1 静态代理的经典实现
静态代理是最基础的代理形式,需要手动创建代理类。假设我们有一个用户服务接口:
java复制public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
对应的静态代理实现如下:
java复制public class UserServiceProxy implements UserService {
private UserService realService;
public UserServiceProxy(UserService realService) {
this.realService = realService;
}
@Override
public void addUser(String username) {
long start = System.currentTimeMillis();
realService.addUser(username);
System.out.println("addUser耗时:" + (System.currentTimeMillis()-start));
}
@Override
public void deleteUser(String username) {
// 权限检查逻辑
if(!checkPermission()) {
throw new SecurityException("无删除权限");
}
realService.deleteUser(username);
}
private boolean checkPermission() {
// 实际的权限检查逻辑
return true;
}
}
静态代理的优点在于实现简单、直观,适合代理逻辑固定的场景。但在实际项目中,它有几个明显的缺点:
- 每个被代理的类都需要单独编写代理类,当接口方法很多时,代码量会急剧膨胀
- 代理逻辑无法复用,相同的增强逻辑(如日志、性能监控)需要在多个代理类中重复编写
- 接口变更时需要同步修改代理类,维护成本高
2.2 动态代理的灵活运用
Java动态代理解决了静态代理的上述痛点。它通过反射机制在运行时动态生成代理类,核心是java.lang.reflect.Proxy类。我们来看一个典型的动态代理实现:
java复制public class LoggingInvocationHandler implements InvocationHandler {
private Object target;
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
System.out.println(method.getName()
