13.设计模式-PROXY(代理)

一、模式定义与核心思想

代理模式是一种结构型设计模式,其核心目标是通过代理对象控制对原始对象的访问,实现职责分离功能增强。该模式通过引入中间层(代理)实现以下核心价值:

  1. 访问控制:限制或保护对敏感对象的直接访问(如权限校验、防火墙代理)
  2. 功能扩展:在不修改原始对象的前提下增加逻辑(如日志记录、性能监控)
  3. 资源优化:延迟大资源加载(虚拟代理)、复用高成本对象(如数据库连接池)
  4. 远程交互:简化分布式系统中远程调用的复杂性(RPC框架、Web服务代理)

核心设计原则

  • 开闭原则:通过代理层扩展功能,避免修改原始对象
  • 单一职责:代理对象专注于非业务逻辑(如安全、缓存),真实对象专注核心功能

二、模式组成与UML类图

核心角色
  1. Subject(抽象主题)
    • 定义真实对象与代理的公共接口(如UserService接口)
  1. RealSubject(真实主题)
    • 实现核心业务逻辑的被代理对象(如UserServiceImpl类)
  1. Proxy(代理)
    • 持有真实对象的引用,控制访问并增强功能(如权限校验、日志记录)
UML类图
classDiagram
    class Subject {
        <<interface>>
        +request()
    }
    
    class RealSubject {
        +request()
    }
    
    class Proxy {
        -realSubject: RealSubject
        +request()
        +preProcess()
        +postProcess()
    }
    
    Subject <|.. RealSubject
    Subject <|.. Proxy
    Proxy --> RealSubject

协作流程

  1. 客户端通过代理对象调用方法
  2. 代理执行预处理(如权限校验)
  3. 代理调用真实对象的核心方法
  4. 代理执行后处理(如日志记录)

三、代码实现示例

场景:实现数据库查询的缓存代理
1. 静态代理实现
// 抽象主题(数据库接口)
interface Database {
    String query(String sql);
}

// 真实主题(MySQL实现)
class MySQLDatabase implements Database {
    @Override
    public String query(String sql) {
        System.out.println("执行SQL查询: " + sql);
        return "查询结果";
    }
}

// 代理类(缓存增强)
class CacheProxy implements Database {
    private Database realDB;
    private Map<String, String> cache = new HashMap<>();

    public CacheProxy(Database db) {
        this.realDB = db;
    }

    @Override
    public String query(String sql) {
        // 缓存检查
        if (cache.containsKey(sql)) {
            System.out.println("【缓存命中】" + sql);
            return cache.get(sql);
        }
        // 真实查询
        String result = realDB.query(sql);
        cache.put(sql, result);
        return result;
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        Database db = new CacheProxy(new MySQLDatabase());
        db.query("SELECT * FROM users");  // 首次查询
        db.query("SELECT * FROM users");  // 命中缓存
    }
}
2. JDK动态代理实现
// 动态代理处理器
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);
    }
}

// 客户端动态生成代理
public class DynamicClient {
    public static void main(String[] args) {
        Database realDB = new MySQLDatabase();
        Database proxy = (Database) Proxy.newProxyInstance(
            realDB.getClass().getClassLoader(),
            realDB.getClass().getInterfaces(),
            new LoggingHandler(realDB)
        );
        proxy.query("SELECT * FROM orders");
    }
}

四、工业级源码应用

  1. Spring AOP
  • 通过JDK动态代理(接口)和CGLIB(类继承)实现事务管理、日志切面
@Transactional
public void updateUser(User user) {
    // 方法调用会被代理增强事务逻辑
}
  1. MyBatis Mapper接口
    • 动态生成Mapper接口的代理实现,将接口方法映射为SQL执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  1. Java RMI(远程方法调用)
    • 通过RemoteObject生成存根(Stub)代理,隐藏网络通信细节
  1. Android Binder机制
    • 跨进程通信时自动生成代理类,封装IPC调用细节
  1. Hibernate延迟加载
    • 使用虚拟代理延迟初始化关联对象,避免N+1查询问题

五、模式对比与选型建议

维度 静态代理 动态代理
实现方式 手动编写代理类 运行时反射生成代理类
灵活性 每新增方法需修改代理类 自动适配接口所有方法
适用场景 简单接口、少量方法 复杂系统、需要统一增强逻辑
性能开销 编译时绑定,执行效率高 反射调用存在性能损耗

最佳实践

  • 简单场景优先选择静态代理(如权限校验代理)
  • 框架开发优先使用动态代理(如Spring AOP)
  • 性能敏感场景考虑字节码增强技术(如ASM、Byte Buddy)

总结

代理模式通过间接访问的设计哲学,在Java生态中展现出强大的生命力。从Spring框架的AOP到MyBatis的Mapper代理,该模式成功解决了横切关注点分离系统扩展性的核心难题。掌握代理模式的关键在于准确识别访问控制需求,并在静态代理的直观性与动态代理的灵活性之间找到平衡点,从而构建出安全、高效且易于维护的系统架构。

posted @ 2025-04-12 10:48  雾里看花的少年  阅读(25)  评论(0)    收藏  举报