【设计模式】【结构型模式】代理模式(Proxy)

简介: 一、入门 什么是代理模式? 代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。 代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,允许你提供一个代理对象来控制对另一个对象的访问。

代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下增加额外的功能或控制访问。

为什么要代理模式?

  1. 违反单一职责原则
    • 原始对象的核心职责是实现业务逻辑,但如果将额外的功能(如权限检查、日志记录等)直接写入原始对象,会导致对象的职责变得复杂,难以维护。
    • 例如,一个UserService类如果既要处理用户登录逻辑,又要记录日志、检查权限,代码会变得臃肿。
  2. 代码重复
    • 如果多个地方需要对对象的访问进行相同的控制(如权限检查),开发者可能会在每个调用点重复编写相同的代码,导致代码冗余。
  3. 难以扩展
    - 如果需要在访问对象时增加新的功能(如缓存、延迟加载等),可能需要直接修改原始对象的代码,这会破坏开闭原则(对扩展开放,对修改关闭)。
    
  4. 性能问题
    • 某些场景下,对象的创建或初始化成本较高(如加载大文件、连接远程服务等)。如果没有代理模式,可能会在不需要时提前创建对象,导致资源浪费。
  5. 安全性问题
    • 如果没有代理模式,客户端可以直接访问敏感对象,可能会绕过必要的安全检查或权限控制。

如何实现代理模式?

  1. Subject(抽象主题):定义真实对象和代理对象的共同接口,客户端通过该接口与真实对象交互。
  2. RealSubject(真实主题):实现Subject接口,是代理对象所代表的真实对象。
  3. Proxy(代理):实现Subject接口,持有对RealSubject的引用,控制对RealSubject的访问,并可以在访问前后添加额外操作。

【案例】订单加强
网购订单处理:假设我们需要在用户下单时记录日志,但不想修改核心的订单处理逻辑。

静态代理

Subject(抽象主题): OrderService接口,定义订单下达。

public interface OrderService {
   
    void createOrder(String product);
}

RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。

public class OrderServiceImpl implements OrderService {
   
    @Override
    public void createOrder(String product) {
   
        System.out.println("订单创建成功,商品:" + product);
    }
}

Proxy(代理)OrderServiceStaticProxy(手动编写代理类,添加日志记录)

public class OrderServiceStaticProxy implements OrderService {
   
    private OrderService orderService;

    public OrderServiceStaticProxy(OrderService orderService) {
   
        this.orderService = orderService;
    }

    @Override
    public void createOrder(String product) {
   
        System.out.println("[静态代理] 记录日志:开始下单");
        orderService.createOrder(product);
        System.out.println("[静态代理] 记录日志:下单完成");
    }
}

测试类

public class Client {
   
    public static void main(String[] args) {
   
        OrderService realService = new OrderServiceImpl();
        OrderService proxy = new OrderServiceStaticProxy(realService);
        proxy.createOrder("iPhone 15");
    }
}

输出

[静态代理] 记录日志:开始下单
订单创建成功,商品:iPhone 15
[静态代理] 记录日志:下单完成

JDK代理

通过JDK的InvocationHandlerProxy类动态生成代理对象。
Subject(抽象主题): OrderService接口,定义订单下达。

public interface OrderService {
   
    void createOrder(String product);
}

RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。

public class OrderServiceImpl implements OrderService {
   
    @Override
    public void createOrder(String product) {
   
        System.out.println("订单创建成功,商品:" + product);
    }
}

Proxy(代理)InvocationHandler类,InvocationHandler接口。

public class LogInvocationHandler implements InvocationHandler {
   
    private Object target; // 真实对象

    public LogInvocationHandler(Object target) {
   
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
        System.out.println("[JDK动态代理] 记录日志:开始执行方法 " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("[JDK动态代理] 记录日志:方法执行完成");
        return result;
    }
}

客户端调用

public class Client {
   
    public static void main(String[] args) {
   
        OrderService realService = new OrderServiceImpl();
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),
            realService.getClass().getInterfaces(),
            new LogInvocationHandler(realService)
        );
        proxy.createOrder("MacBook Pro");
    }
}

输出结果

[JDK动态代理] 记录日志:开始执行方法 createOrder
订单创建成功,商品:MacBook Pro
[JDK动态代理] 记录日志:方法执行完成

CGLIB动态代理

通过继承目标类生成子类来实现代理(无需接口)
RealSubject(真实主题)OrderServiceImpl类,实现下单逻辑。(这个增强无需接口)

// 真实对象(无需接口)
public class OrderService {
   
    public void createOrder(String product) {
   
        System.out.println("订单创建成功,商品:" + product);
    }
}

Proxy(代理)MethodInterceptor

public class LogMethodInterceptor implements MethodInterceptor {
   
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
   
        System.out.println("[CGLIB代理] 记录日志:开始执行方法 " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("[CGLIB代理] 记录日志:方法执行完成");
        return result;
    }
}

客户端

public class Client {
   
    public static void main(String[] args) {
   
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class); // 设置父类
        enhancer.setCallback(new LogMethodInterceptor()); // 设置回调
        OrderService proxy = (OrderService) enhancer.create(); // 创建代理对象
        proxy.createOrder("AirPods Pro");
    }
}

输出结果

[CGLIB代理] 记录日志:开始执行方法 createOrder
订单创建成功,商品:AirPods Pro
[CGLIB代理] 记录日志:方法执行完成

三种代理模式对比

代理类型 特点 适用场景
静态代理 手动编写代理类,直接调用目标对象。 代理逻辑简单,目标对象固定。
JDK动态代理 基于接口动态生成代理类,通过反射调用目标方法。 需要代理接口的实现类。
CGLIB代理 通过继承目标类生成子类代理,无需接口。性能较高,但生成代理类较慢。 需要代理没有实现接口的类。

适用场景

  • 静态代理:适合代理逻辑简单且目标对象固定的场景。
  • JDK动态代理:适合基于接口的代理(如Spring AOP默认使用JDK代理)。
  • CGLIB代理:适合代理没有接口的类(如Spring AOP在类没有接口时自动切换为CGLIB)。

二、代理模式在框架源码中的运用

Spring Framework 中的 AOP 代理

Spring AOP 通过代理模式实现方法拦截(如事务管理、日志记录),核心类是 ProxyFactoryBean 和动态代理生成器。

核心角色与类:

  • Subject(抽象主题):被代理的接口或类(如 UserService 接口)。
  • RealSubject(真实主题):实际的目标对象(如 UserServiceImpl 类)。
  • Proxy(代理):由 Spring 动态生成的代理对象,具体分为两类:
    • JDK 动态代理:通过 JdkDynamicAopProxy 类生成(代理接口)。
  • CGLIB 代理:通过 ObjenesisCglibAopProxy 类生成(代理类,无接口时使用)。

    // Spring AOP 生成代理的核心逻辑(ProxyFactoryBean)
    public class ProxyFactoryBean {
         
      private Object target;          // RealSubject(真实对象)
      private Class<?>[] interfaces;  // Subject(接口)
      private Advice advice;          // 增强逻辑(如事务、日志)
    
      public Object getObject() {
         
          // 根据配置选择生成 JDK 或 CGLIB 代理
          return createAopProxy().getProxy();
      }
    }
    

    执行流程:

  1. 客户端调用代理对象的方法(如userService.save())。
  2. 代理对象拦截方法调用,执行增强逻辑(如开启事务)。
  3. 代理对象通过反射调用真实对象的方法(UserServiceImpl.save())。
  4. 返回结果前,执行后置增强逻辑(如提交事务)。

MyBatis 中的 Mapper 接口代理

MyBatis 通过代理模式将 Mapper 接口的方法调用转换为 SQL 执行,核心类是 MapperProxy

核心角色与类

  • Subject(抽象主题)Mapper 接口(如 UserMapper)。
  • RealSubject(真实主题):不存在真实实现类,由代理直接处理逻辑。
  • Proxy(代理)MapperProxy 类,实现 InvocationHandler 接口,动态代理 Mapper 接口。
// MyBatis 的 Mapper 代理生成逻辑(MapperProxyFactory)
public class MapperProxyFactory<T> {
   
    private final Class<T> mapperInterface;

    public T newInstance(SqlSession sqlSession) {
   
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    mapperInterface }, mapperProxy);
    }
}

// MapperProxy 实现 InvocationHandler
public class MapperProxy<T> implements InvocationHandler {
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
        // 将方法调用转换为 SQL 执行(如执行 select * from user where id = ?)
        return execute(method, args);
    }
}
`

执行流程

  1. 客户端调用 UserMapper.findById(1)
  2. MapperProxy 拦截方法调用,解析方法名和参数。
  3. 根据方法名找到对应的 SQL 语句并执行。
  4. 返回数据库查询结果。

三、总结

代理模式的优点

  1. 职责清晰
    • 代理对象负责处理与核心业务无关的逻辑(如权限检查、日志记录、延迟加载等),而真实对象只需关注核心业务逻辑。
    • 符合单一职责原则。
  2. 增强功能
    • 在不修改真实对象的情况下,通过代理对象增强功能(如事务管理、缓存、延迟加载等)。
  3. 解耦
    • 代理模式将客户端与真实对象解耦,客户端只需与代理对象交互,无需直接访问真实对象。
  4. 安全性
    • 代理对象可以控制对真实对象的访问,增加权限检查等安全措施。
  5. 灵活性
    • 动态代理(如 JDK 动态代理、CGLIB 代理)可以在运行时动态生成代理对象,适应不同的需求。

代理模式的缺点

  1. 复杂性增加
    • 引入代理对象会增加系统的复杂性,尤其是动态代理的实现需要理解反射和字节码生成技术。
  2. 性能开销
    • 代理模式可能会引入额外的性能开销,尤其是在频繁调用时(如动态代理的反射调用)。
  3. 代码冗余
    • 静态代理需要为每个真实对象编写代理类,可能导致代码冗余。
  4. 调试困难
    • 动态代理生成的代理类在运行时才存在,调试时可能不如静态代理直观。

代理模式的适用场景

  1. 远程代理
    • 为位于不同地址空间的对象提供本地代表(如 RPC 框架中的远程服务调用)。
  2. 虚拟代理
    • 延迟创建开销较大的对象,直到真正需要时才创建(如图片懒加载、大文件加载)。
  3. 保护代理
    • 控制对敏感对象的访问,基于权限决定是否允许访问(如权限校验)。
  4. 智能引用代理
    • 在访问对象时执行额外操作(如引用计数、懒加载、缓存等)。
  5. AOP(面向切面编程)
    • 在方法调用前后增加通用逻辑(如日志记录、事务管理、性能监控等)。
  6. 延迟初始化
    • 在需要时才初始化对象,节省资源(如 Hibernate 的延迟加载)。
  7. 简化客户端调用
    • 客户端只需与代理对象交互,无需关心真实对象的复杂性(如 MyBatis 的 Mapper 代理)。
目录
相关文章
|
4月前
|
设计模式 存储 缓存
【设计模式】【结构型模式】享元模式(Flyweight)
一、入门 什么是享元模式? 享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。 它的核心思想是将对象的内在状态(不变
172 16
|
4月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
139 10
|
4月前
|
关系型数据库 Java MySQL
【设计模式】【结构型模式】桥接模式(Bridge)
一、入门 什么是桥接模式? 桥接模式(Bridge Pattern)是一种结构型设计模式,核心思想是将抽象与实现分离,让它们可以独立变化。简单来说,它像一座“桥”连接了两个维度的变化,避免用继承导致代
296 10
|
4月前
|
设计模式 前端开发 Java
【设计模式】【结构型模式】适配器模式(Adpter)
一、入门 什么是适配器模式? 适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。 为什么要用适配器模式? 假设我们需要在电商系
112 10
|
4月前
|
设计模式 Java 数据库连接
【设计模式】【结构型模式】外观模式(Facde)
一、入门 什么是外观模式? 一种结构型设计模式,通过为子系统中的一组接口提供一个统一的高层接口(称为外观),来简化客户端与复杂子系统的交互过程。其本质是建立抽象层来隔离复杂度。 为什么要有外观模式?
199 9
|
4月前
|
设计模式 缓存 安全
【设计模式】【结构型模式】装饰者模式(Decorator)
一、入门 什么是装饰者模式? 装饰者模式(Decorator Pattern)是 Java 中常用的结构型设计模式,它能在不修改原有对象结构的前提下,动态地为对象添加额外的职责。 为什么要装饰者模式?
83 8
|
8月前
|
设计模式 缓存 Java
「全网最细 + 实战源码案例」设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对目标对象的访问并添加额外功能。它分为静态代理和动态代理,后者包括JDK动态代理和CGLIB动态代理。JDK动态代理基于接口反射生成代理类,而CGLIB通过继承目标类生成子类。代理模式适用于延迟初始化、访问控制、远程服务、日志记录和缓存等场景,优点是职责分离、符合开闭原则和提高安全性,缺点是增加系统复杂性。
198 25
|
9月前
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式
|
12月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
4月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
127 16