代理模式 (Proxy Pattern)
为其他对象提供一种代理以控制对这个对象的访问
模式定义
代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过创建一个代理对象来代表真实对象,客户端通过代理对象间接访问真实对象。代理模式可以在不改变原始对象的情况下,增加额外的功能操作,如访问控制、延迟加载、日志记录等。
模式结构
graph TD
A[Client] --> B((Subject))
B --> C[RealSubject]
B --> D[Proxy]
D --> C
实现方式
基础实现
定义主题接口,实现真实主题和代理类。
// 主题接口
public interface Image {
void display();
}
// 真实主题类
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("从磁盘加载图片: " + filename);
// 模拟加载时间
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void display() {
System.out.println("显示图片: " + filename);
}
}
// 代理类
public class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
使用示例
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image1 = new ProxyImage("photo1.jpg");
Image image2 = new ProxyImage("photo2.jpg");
// 第一次显示图片,会加载图片
System.out.println("第一次显示图片:");
image1.display();
System.out.println();
// 第二次显示图片,直接显示,无需加载
System.out.println("第二次显示图片:");
image1.display();
System.out.println();
// 显示另一张图片
System.out.println("显示第二张图片:");
image2.display();
}
}
保护代理示例
使用代理模式实现访问控制。
// 主题接口
public interface Internet {
void connectTo(String serverHost) throws Exception;
}
// 真实主题类
public class RealInternet implements Internet {
@Override
public void connectTo(String serverHost) {
System.out.println("连接到 " + serverHost);
}
}
// 保护代理类
import java.util.ArrayList;
import java.util.List;
public class ProxyInternet implements Internet {
private Internet internet = new RealInternet();
private static List bannedSites;
static {
bannedSites = new ArrayList<>();
bannedSites.add("badsite.com");
bannedSites.add("restricted.com");
bannedSites.add("illegal.com");
}
@Override
public void connectTo(String serverHost) throws Exception {
if (bannedSites.contains(serverHost.toLowerCase())) {
throw new Exception("访问被拒绝: " + serverHost);
}
internet.connectTo(serverHost);
}
}
// 客户端使用
public class ProtectionProxyDemo {
public static void main(String[] args) {
Internet internet = new ProxyInternet();
try {
internet.connectTo("google.com");
internet.connectTo("badsite.com");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
经典案例
Java 动态代理
Java 提供了动态代理机制,可以在运行时创建代理对象。
// 接口定义
public interface UserService {
void saveUser(String username);
String findUser(int id);
}
// 真实主题实现
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String username) {
System.out.println("保存用户: " + username);
}
@Override
public String findUser(int id) {
System.out.println("查找用户 ID: " + id);
return "User" + id;
}
}
// 动态代理处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceProxy implements InvocationHandler {
private Object target;
public UserServiceProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始执行方法: " + method.getName());
// 记录方法执行时间
long startTime = System.currentTimeMillis();
// 调用真实对象的方法
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("方法执行完成: " + method.getName() + ", 耗时: " + (endTime - startTime) + "ms");
return result;
}
// 创建代理对象的工厂方法
public static T createProxy(T target, Class interfaceClass) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{interfaceClass},
new UserServiceProxy(target)
);
}
}
// 客户端使用
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建真实对象
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = UserServiceProxy.createProxy(userService, UserService.class);
// 通过代理对象调用方法
proxy.saveUser("张三");
System.out.println();
String user = proxy.findUser(1);
System.out.println("找到用户: " + user);
}
}
Spring AOP 中的代理
Spring AOP 使用代理模式实现面向切面编程。
// 业务接口
public interface BankService {
void transfer(String fromAccount, String toAccount, double amount);
double getBalance(String account);
}
// 业务实现类
public class BankServiceImpl implements BankService {
@Override
public void transfer(String fromAccount, String toAccount, double amount) {
System.out.println("转账: " + amount + " 从 " + fromAccount + " 到 " + toAccount);
// 模拟转账操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public double getBalance(String account) {
System.out.println("查询账户余额: " + account);
return 1000.0; // 模拟余额
}
}
// 日志代理
public class LoggingProxy implements BankService {
private BankService bankService;
public LoggingProxy(BankService bankService) {
this.bankService = bankService;
}
@Override
public void transfer(String fromAccount, String toAccount, double amount) {
System.out.println("=== 开始转账操作 ===");
long startTime = System.currentTimeMillis();
bankService.transfer(fromAccount, toAccount, amount);
long endTime = System.currentTimeMillis();
System.out.println("=== 转账完成,耗时: " + (endTime - startTime) + "ms ===");
}
@Override
public double getBalance(String account) {
System.out.println("=== 开始查询余额 ===");
double balance = bankService.getBalance(account);
System.out.println("=== 查询完成 ===");
return balance;
}
}
// 安全代理
public class SecurityProxy implements BankService {
private BankService bankService;
private String currentUser;
public SecurityProxy(BankService bankService, String currentUser) {
this.bankService = bankService;
this.currentUser = currentUser;
}
@Override
public void transfer(String fromAccount, String toAccount, double amount) {
if (isAuthorized(fromAccount)) {
bankService.transfer(fromAccount, toAccount, amount);
} else {
System.out.println("权限不足,无法执行转账操作");
}
}
@Override
public double getBalance(String account) {
if (isAuthorized(account)) {
return bankService.getBalance(account);
} else {
System.out.println("权限不足,无法查询余额");
return 0.0;
}
}
private boolean isAuthorized(String account) {
// 简单的权限检查
return account.startsWith(currentUser);
}
}
// 客户端使用
public class SpringAOPDemo {
public static void main(String[] args) {
// 创建真实服务
BankService bankService = new BankServiceImpl();
// 添加日志代理
BankService loggingProxy = new LoggingProxy(bankService);
// 添加安全代理
BankService securityProxy = new SecurityProxy(loggingProxy, "user1");
// 执行业务操作
securityProxy.transfer("user1-account1", "user2-account1", 500.0);
System.out.println();
securityProxy.getBalance("user1-account1");
System.out.println();
// 尝试未授权操作
securityProxy.transfer("user2-account1", "user1-account1", 100.0);
}
}
适用场景
- 远程代理:为一个位于不同地址空间的对象提供本地代表,隐藏对象位于不同地址空间的事实
- 虚拟代理:根据需要创建开销很大的对象,通过代理延迟对象的创建,直到真正需要时才创建
- 保护代理:控制对原始对象的访问,根据调用者的权限决定是否允许访问
- 智能引用代理:当对象被引用时,提供一些额外的操作,如引用计数、延迟加载等
- 防火墙代理:保护目标对象不被恶意用户访问
- 同步代理:在多线程环境中为对象提供同步控制
- 复杂性隐藏代理:隐藏复杂对象系统的复杂性
优缺点
优点
- 职责清晰:真实对象专注于业务逻辑,代理对象负责其他额外功能,符合单一职责原则
- 高扩展性:代理模式可以在不修改原始对象的情况下增加功能,符合开闭原则
- 智能化控制:代理可以在运行时根据条件决定是否调用真实对象,实现智能化控制
- 降低系统耦合度:代理模式能够协调调用者和被调用者,在一定程度上降低系统的耦合度
- 增强系统安全性:通过代理可以控制对真实对象的访问权限
缺点
- 请求速度变慢:由于增加了代理对象,可能会导致请求处理速度变慢
- 系统复杂性增加:引入代理模式会增加系统的复杂性,需要额外的代理类
- 可能造成循环调用:在某些情况下,代理和真实对象之间可能会出现循环调用
- 实现代理模式需要额外的工作:有些类型的代理模式实现起来非常复杂