代理模式:控制对象访问的智能代表
摘要
代理模式(Proxy Pattern)是结构型设计模式中的"访问控制器",它通过创建代理对象来控制对原始对象的访问。本文将深入剖析代理模式的核心概念、实现方式、应用场景及高级变体,通过丰富的Java代码示例展示如何构建灵活的对象访问控制机制,并分析其与装饰器模式、适配器模式的区别与适用场景。
一、代理模式核心思想
代理模式的核心是控制访问,具有以下关键特征:
- 间接访问:通过代理对象间接访问目标对象
- 访问控制:代理对象控制对真实对象的访问权限
- 功能增强:可在不修改原对象的情况下添加额外功能
- 接口一致性:代理与真实对象实现相同接口
适用场景:
- 需要控制对对象的访问权限
- 需要延迟加载或远程访问
- 需要添加对象访问的额外逻辑(如日志、监控)
- 需要为消耗资源较大的对象提供轻量级代表
二、代理模式结构解析
UML类图示意
[Subject] <|-- [RealSubject]
[Subject] <|-- [Proxy]
[Proxy] o--> [RealSubject]
核心组件角色
| 角色 | 职责 | 典型实现 |
|---|---|---|
| Subject | 抽象主题 | 定义真实主题和代理的公共接口 |
| RealSubject | 真实主题 | 定义代理所代表的真实对象 |
| Proxy | 代理 | 维护对真实主题的引用,控制访问 |
三、基础实现:图片加载案例
// 抽象主题
interface Image {
void display();
}
// 真实主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
// 模拟耗时加载
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 代理:虚拟代理(延迟加载)
class ProxyImage implements Image {
private String filename;
private RealImage realImage;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 客户端使用
public class ImageViewer {
public static void main(String[] args) {
Image image1 = new ProxyImage("photo1.jpg");
Image image2 = new ProxyImage("photo2.jpg");
// 图片未加载
System.out.println("Images created, not loaded yet");
// 第一次显示需要加载
image1.display();
// 第二次显示不需要加载
image1.display();
// 另一张图片
image2.display();
}
}
四、高级应用:动态代理机制
1. JDK动态代理
// 抽象主题
interface UserService {
void addUser(String username);
void deleteUser(String username);
}
// 真实主题
class UserServiceImpl implements UserService {
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
public void deleteUser(String username) {
System.out.println("Deleting user: " + username);
}
}
// 调用处理器
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);
long duration = System.currentTimeMillis() - start;
System.out.printf("Executed %s in %d ms\n", method.getName(), duration);
return result;
}
}
// 使用示例
public class DynamicProxyDemo {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LoggingInvocationHandler(realService)
);
proxy.addUser("Alice");
proxy.deleteUser("Bob");
}
}
2. CGLIB动态代理
// 真实主题(无需接口)
class ProductService {
public void saveProduct(String name) {
System.out.println("Saving product: " + name);
}
}
// 方法拦截器
class TransactionInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Starting transaction");
Object result = proxy.invokeSuper(obj, args);
System.out.println("Committing transaction");
return result;
}
}
// 使用示例
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback(new TransactionInterceptor());
ProductService proxy = (ProductService) enhancer.create();
proxy.saveProduct("Laptop");
}
}
五、代理模式类型对比
代理类型对比表
| 代理类型 | 特点 | 适用场景 | 实现复杂度 |
|---|---|---|---|
| 虚拟代理 | 延迟加载 | 资源消耗大的对象 | 中等 |
| 保护代理 | 访问控制 | 权限敏感对象 | 中等 |
| 远程代理 | 网络通信 | 远程对象调用 | 高 |
| 缓存代理 | 结果缓存 | 重复计算场景 | 中等 |
| 动态代理 | 运行时生成 | AOP编程 | 高 |
代理模式 vs 装饰器模式
| 维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问 | 增强功能 |
| 对象创建 | 通常由代理控制 | 通常由客户端控制 |
| 关注点 | 访问机制 | 附加功能 |
| 生命周期 | 代理可管理目标生命周期 | 装饰器与组件独立 |
| 典型应用 | 延迟加载、访问控制 | 流处理、UI组件 |
六、企业级应用案例
1. Spring AOP代理
// 使用Spring AOP实现日志代理
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + duration + "ms");
return result;
}
}
// 配置类
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example")
public class AppConfig {
}
2. RPC框架远程代理
// RPC客户端代理
public class RpcProxy implements InvocationHandler {
private Class<?> interfaceClass;
private String host;
private int port;
public RpcProxy(Class<?> interfaceClass, String host, int port) {
this.interfaceClass = interfaceClass;
this.host = host;
this.port = port;
}
public Object newInstance() {
return Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class[]{interfaceClass},
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 构造请求
RpcRequest request = new RpcRequest();
request.setClassName(interfaceClass.getName());
request.setMethodName(method.getName());
request.setParams(args);
request.setParamTypes(method.getParameterTypes());
// 发送网络请求
return sendRequest(request);
}
private Object sendRequest(RpcRequest request) {
try (Socket socket = new Socket(host, port);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
oos.writeObject(request);
return ois.readObject();
} catch (Exception e) {
throw new RuntimeException("RPC call failed", e);
}
}
}
// 使用示例
public class RpcClient {
public static void main(String[] args) {
UserService userService = (UserService) new RpcProxy(
UserService.class, "localhost", 8080).newInstance();
userService.addUser("Alice");
}
}
七、代理模式最佳实践
1. 延迟初始化代理
// 线程安全的延迟初始化代理
class LazyInitializationProxy<T> implements InvocationHandler {
private volatile T target;
private Supplier<T> supplier;
private final Object lock = new Object();
public LazyInitializationProxy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target == null) {
synchronized (lock) {
if (target == null) {
target = supplier.get();
}
}
}
return method.invoke(target, args);
}
public static <T> T create(Class<T> interfaceClass, Supplier<T> supplier) {
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class[]{interfaceClass},
new LazyInitializationProxy<>(supplier)
);
}
}
// 使用示例
DatabaseService heavyDBService = LazyInitializationProxy.create(
DatabaseService.class,
() -> new HeavyDatabaseService()
);
2. 访问控制代理
// 保护代理实现
class AccessControlProxy implements InvocationHandler {
private Object target;
private User user;
public AccessControlProxy(Object target, User user) {
this.target = target;
this.user = user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.isAnnotationPresent(RequiresPermission.class)) {
RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
if (!user.hasPermission(annotation.value())) {
throw new SecurityException("Access denied for " + method.getName());
}
}
return method.invoke(target, args);
}
}
// 权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface RequiresPermission {
String value();
}
// 使用示例
public class SecureSystem {
public static void main(String[] args) {
User admin = new User("admin", Set.of("READ", "WRITE", "DELETE"));
User guest = new User("guest", Set.of("READ"));
DocumentService docService = new DocumentServiceImpl();
DocumentService adminProxy = createProxy(docService, admin);
DocumentService guestProxy = createProxy(docService, guest);
adminProxy.readDocument(); // OK
adminProxy.deleteDocument(); // OK
guestProxy.readDocument(); // OK
guestProxy.deleteDocument(); // 抛出SecurityException
}
private static DocumentService createProxy(DocumentService target, User user) {
return (DocumentService) Proxy.newProxyInstance(
DocumentService.class.getClassLoader(),
new Class[]{DocumentService.class},
new AccessControlProxy(target, user)
);
}
}
八、代理模式在开源框架中的应用
MyBatis的Mapper代理
// MyBatis通过动态代理实现Mapper接口
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
// 底层实现原理
public class MapperProxy<T> implements InvocationHandler {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
public Object invoke(Object proxy, Method method, Object[] args) {
// 将方法调用转换为SQL执行
String statement = mapperInterface.getName() + "." + method.getName();
return sqlSession.selectOne(statement, args[0]);
}
}
Hibernate延迟加载
// Hibernate实体延迟加载代理
Session session = sessionFactory.openSession();
Product product = session.load(Product.class, 1L); // 返回代理对象
// 实际访问时加载数据
System.out.println(product.getName()); // 触发数据库查询
九、代理模式未来发展趋势
新兴应用方向:
- 微服务网关:API网关作为服务的代理
- 服务网格:Sidecar代理服务间通信
- 云函数:函数计算的事件代理
- 区块链智能合约:合约调用代理
- AI模型服务:模型推理代理
响应式代理模式
// Reactor中的代理模式应用
class ReactiveProxy<T> {
private final Mono<T> target;
public ReactiveProxy(Mono<T> target) {
this.target = target.cache();
}
public Mono<Object> invoke(String methodName, Object... args) {
return target.flatMap(instance -> {
try {
Method method = instance.getClass().getMethod(methodName);
return Mono.fromCallable(() -> method.invoke(instance, args));
} catch (Exception e) {
return Mono.error(e);
}
});
}
}
// 使用示例
ReactiveProxy<HeavyService> proxy = new ReactiveProxy<>(
Mono.fromCallable(HeavyService::new).subscribeOn(Schedulers.boundedElastic())
);
proxy.invoke("expensiveOperation")
.subscribe(result -> System.out.println("Result: " + result));
总结
代理模式是控制对象访问的强大工具,特别适合需要间接管理对象访问的场景。其核心价值体现在:
- 访问控制:精细控制对敏感对象的访问
- 功能增强:透明添加额外逻辑
- 资源优化:延迟加载和缓存优化资源使用
- 分布式支持:简化远程对象访问
现代应用关键点:
- 合理选择代理类型:根据场景选择静态/动态代理
- 性能考量:避免过度代理导致性能下降
- 安全边界:保护代理需严格验证权限
- 与AOP结合:利用AOP简化代理创建
- 响应式支持:适应异步非阻塞编程模型
代理模式正在与云原生、服务网格等现代架构结合,演进出更强大的访问控制能力。掌握代理模式的精髓,将帮助开发者构建出更加安全、高效的系统架构。
















