模式定义

代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过创建一个代理对象来代表真实对象,客户端通过代理对象间接访问真实对象。代理模式可以在不改变原始对象的情况下,增加额外的功能操作,如访问控制、延迟加载、日志记录等。

模式结构

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);
    }
}

适用场景

优缺点

优点

  • 职责清晰:真实对象专注于业务逻辑,代理对象负责其他额外功能,符合单一职责原则
  • 高扩展性:代理模式可以在不修改原始对象的情况下增加功能,符合开闭原则
  • 智能化控制:代理可以在运行时根据条件决定是否调用真实对象,实现智能化控制
  • 降低系统耦合度:代理模式能够协调调用者和被调用者,在一定程度上降低系统的耦合度
  • 增强系统安全性:通过代理可以控制对真实对象的访问权限

缺点

  • 请求速度变慢:由于增加了代理对象,可能会导致请求处理速度变慢
  • 系统复杂性增加:引入代理模式会增加系统的复杂性,需要额外的代理类
  • 可能造成循环调用:在某些情况下,代理和真实对象之间可能会出现循环调用
  • 实现代理模式需要额外的工作:有些类型的代理模式实现起来非常复杂