代理模式(Proxy Pattern)

定义

适配器模式是一种​​结构型设计模式​​,用于解决两个不兼容接口之间的兼容性问题。它通过​​包装对象​​的方式,将已有接口转换为客户端期望的接口,类似电源适配器的功能。


UML 类图​

设计模式之结构型:代理模式_代理模式

核心角色

  • Subject:抽象接口,定义真实对象和代理对象的共同行为(如 request())。
  • RealSubject:真实对象,实现核心业务逻辑(如数据库查询、文件操作)。
  • Proxy:代理对象,控制对 RealSubject 的访问(如权限验证、延迟加载)。
  • Client:客户端,通过 Subject 接口调用功能,无需感知代理与真实对象的区别。

优点​

  • ​​隔离性​​:客户端与真实对象解耦,避免直接操作敏感资源(如数据库连接)。
  • ​​功能增强​​:在不修改真实对象的前提下,添加额外逻辑(如日志记录、性能监控)。
  • ​​性能优化​​:延迟加载大对象(如图片、视频),减少系统启动时的资源消耗。
  • ​​开闭原则​​:新增代理类不影响已有代码,符合扩展开放、修改关闭的原则。

缺点​​

  • 复杂度增加​​:引入代理层可能导致系统结构更复杂(尤其是多重代理)。
  • ​​间接调用开销​​:代理转发请求可能增加响应时间(远程代理的网络延迟更明显)。
  • ​​代码冗余​​:静态代理需要为每个类单独编写代理(动态代理可解决此问题)。

适用场景​

虚拟代理(延迟加载)​​

加载大文件或复杂对象时,先用代理占位,按需加载真实对象。

// 示例:图片代理延迟加载
Image proxyImage = new ProxyImage("large_image.jpg");
proxyImage.display();  // 仅在调用时加载真实图片
  • 1.
  • 2.
  • 3.

保护代理(权限控制)

控制客户端对敏感资源的访问(如管理员接口、支付功能)。

if (user.isAdmin()) {
    realSubject.deleteData();  // 仅允许管理员执行
}
  • 1.
  • 2.
  • 3.

​​远程代理(网络通信)​​

为远程服务(如 REST API、RPC)提供本地代理,隐藏通信细节。

// 客户端调用本地代理,实际请求发送到远程服务器
PaymentService proxy = new RemotePaymentProxy();
proxy.pay(100);  
  • 1.
  • 2.
  • 3.

缓存代理(性能优化)

缓存频繁访问的数据(如数据库查询结果、API 响应)。

if (cache.has("query_result")) {
    return cache.get("query_result");  // 直接返回缓存
} else {
    realSubject.query();  // 首次查询后缓存结果
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

​​日志与监控​​

记录请求日志或统计方法执行时间。

long start = System.currentTimeMillis();
realSubject.process();  // 执行真实逻辑
long duration = System.currentTimeMillis() - start;
log.info("方法执行耗时:" + duration + "ms");
  • 1.
  • 2.
  • 3.
  • 4.

​​# 代码示例(Java)​​

​​静态代理(显式实现代理类)​

// 抽象接口
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("加载图片: " + filename);
    }

    @Override
    public void display() {
        System.out.println("显示图片: " + filename);
    }
}

// 代理对象(延迟加载)
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 Client {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        image.display();  // 输出:加载图片: test.jpg → 显示图片: test.jpg
    }
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.

动态代理(JDK 原生实现)​​

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象接口
interface UserService {
    void save();
}

// 真实对象
class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户数据");
    }
}

// 动态代理处理器
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 Client {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),
            realService.getClass().getInterfaces(),
            new LoggingHandler(realService)  // 自动生成代理类
        );
        proxy.save();  // 输出:记录日志 → 保存用户数据
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.

​​​​两种代理类型对比​​

设计模式之结构型:代理模式_代理模式_02

​​​​实际应用案例​​

Spring AOP​​

基于动态代理实现事务管理(@Transactional)和日志切面。

​​Hibernate 延迟加载​​

使用虚拟代理实现数据库关联对象的按需加载(Lazy Loading)。

RPC 框架(如 Dubbo)​​

远程代理将本地调用透明转发到远程服务。

安全框架(如 Shiro)​​

保护代理控制对敏感方法的访问权限。

​​总结​​:代理模式通过中间层控制对象访问,是增强系统安全性和灵活性的重要手段。​​优先选择动态代理​​(减少代码冗余),在需要精细控制时使用静态代理。