【西瓜带你学设计模式 | 第九期 - 代理模式】代理模式 —— 静态与动态代理实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 代理模式是什么?](#1. 代理模式是什么?)
    • [2. 代理模式解决什么问题?](#2. 代理模式解决什么问题?)
    • [3. 代理模式的实现思路](#3. 代理模式的实现思路)
    • [4. 静态代理(Static Proxy)](#4. 静态代理(Static Proxy))
      • [4.1 场景示例:给真实对象加"日志与权限"](#4.1 场景示例:给真实对象加“日志与权限”)
      • [4.2 静态代理特点](#4.2 静态代理特点)
    • [5. 动态代理(JDK 动态代理)](#5. 动态代理(JDK 动态代理))
      • [5.1 定义接口与真实对象](#5.1 定义接口与真实对象)
      • [5.2 动态代理的核心:InvocationHandler](#5.2 动态代理的核心:InvocationHandler)
      • [5.3 动态代理特点](#5.3 动态代理特点)
    • [6. 常见类型与适用场景](#6. 常见类型与适用场景)
    • [7. 优缺点](#7. 优缺点)
      • [7.1 代理模式优点](#7.1 代理模式优点)
      • [7.2 代理模式缺点](#7.2 代理模式缺点)
      • [7.3 和装饰器模式的简单区分](#7.3 和装饰器模式的简单区分)
    • [8. 总结](#8. 总结)

前言

在面向对象设计里,"不直接动这个对象,但要在访问它之前/之后做一些事"是非常常见的需求。代理模式(Proxy Pattern) 就是用来解决这种问题的:

让一个对象充当"中介",把对另一个对象的访问过程包装起来。

可以把它理解成:想上门见客户,但先经过前台/经纪人/中介 ------真正的客户对象不需要直接暴露给你。


1. 代理模式是什么?

代理模式: 为其他对象提供一种代理,以便控制对这个对象的访问。

核心思想:

  • 真实对象(Real Subject)负责"实际业务"
  • 代理对象(Proxy)负责"控制访问 + 增强行为"
  • 客户端(Client)面向代理编程,而不是面向真实对象直接调用

GoF 的经典结构里通常有:

  • Subject:抽象主题(接口/抽象类)
  • RealSubject:真实主题(核心实现)
  • Proxy:代理主题(增强与控制)

2. 代理模式解决什么问题?

代理模式主要用来实现这些能力:

  1. 权限控制/访问校验

    例如:管理员才能调用某些方法。

  2. 日志/埋点/审计

    例如:统计方法耗时、记录调用参数与结果。

  3. 远程调用封装

    例如:RPC 框架里的 Stub/Client Proxy,看起来像本地调用。

  4. 缓存/延迟加载

    例如:首次访问才创建真实对象,后续直接命中缓存。

  5. 增强与解耦

    例如:不改真实对象代码,也能叠加横切逻辑(横切关注点)。


3. 代理模式的实现思路

实现代理模式时,常见套路是:

  • 先定义统一接口 Subject
  • RealSubject 实现真实业务
  • Proxy 内部持有一个 RealSubject 引用
  • Proxy 的方法里:
    • 访问前做增强(权限/日志/计时)
    • 再调用真实对象
    • 访问后做增强(记录结果/清理资源/统计)

4. 静态代理(Static Proxy)

4.1 场景示例:给真实对象加"日志与权限"

假设有一个接口:Service

java 复制代码
public interface Service {
    void doWork();
}

真实对象:RealService

java 复制代码
public class RealService implements Service {
    @Override
    public void doWork() {
        System.out.println("RealService: 执行业务逻辑");
    }
}

代理对象:ServiceProxy

java 复制代码
public class ServiceProxy implements Service {
    private final Service real;

    public ServiceProxy(Service real) {
        this.real = real;
    }

    @Override
    public void doWork() {
        // 1) 访问前增强:权限校验
        System.out.println("Proxy: 权限校验通过(示例)");

        // 2) 访问前增强:日志/计时
        long start = System.currentTimeMillis();

        // 3) 调用真实对象
        real.doWork();

        // 4) 访问后增强:日志/统计
        long cost = System.currentTimeMillis() - start;
        System.out.println("Proxy: doWork 耗时 = " + cost + "ms");
    }
}

客户端使用代理:

java 复制代码
public class Client {
    public static void main(String[] args) {
        Service real = new RealService();
        Service proxy = new ServiceProxy(real);

        proxy.doWork();
    }
}

4.2 静态代理特点

  • 代理类由程序员手写(或生成)
  • 编译期就确定代理逻辑
  • 适合代理逻辑稳定、对象数量不太多的场景

5. 动态代理(JDK 动态代理)

当目标对象实现了接口,可以用 java.lang.reflect.Proxy 做动态代理。

5.1 定义接口与真实对象

接口同上:Service

真实对象:RealService

5.2 动态代理的核心:InvocationHandler

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK Proxy: 权限校验(示例)");

                        long start = System.currentTimeMillis();
                        Object result = method.invoke(target, args);
                        long cost = System.currentTimeMillis() - start;

                        System.out.println("JDK Proxy: " + method.getName() + " 耗时=" + cost + "ms");
                        return result;
                    }
                }
        );
    }
}

客户端:

java 复制代码
public class Client {
    public static void main(String[] args) {
        Service real = new RealService();
        Service proxy = JdkProxyFactory.createProxy(real);

        proxy.doWork();
    }
}

5.3 动态代理特点

  • 代理逻辑在运行时决定(通过反射调用)
  • 代理类不需要手写每一种类型
  • 但性能上一般比静态代理略逊(反射开销)

6. 常见类型与适用场景

按"代理方式"可以分为:

  1. 静态代理

    • 手写代理类
    • 场景:权限、日志、缓存等增强固定且简单
  2. JDK 动态代理

    • 目标必须实现接口
    • 场景:框架通用增强(例如 AOP、RPC 客户端代理)
  3. CGLIB 动态代理

    • 目标类无需实现接口
    • 场景:类代理(Spring 某些配置下)
  4. 远程代理(Remote Proxy)

    • 客户端调用像本地一样,但底层走网络
    • 典型:RPC 的 client stub
  5. 虚拟代理(Virtual Proxy)

    • 延迟初始化:首次访问才创建真实对象
    • 场景:大对象、图片/资源加载、懒加载
  6. 保护代理(Protection Proxy)

    • 用于访问控制(角色/令牌校验)
    • 场景:后台管理接口、敏感操作保护
  7. 缓存代理(Caching Proxy)

    • 先查缓存,命中则直接返回,不命中再调用真实对象
    • 场景:热点查询、幂等读

7. 优缺点

7.1 代理模式优点

  • 可控访问:能在调用前后加逻辑(权限、日志、审计)
  • 增强解耦:真实对象专注核心业务,横切逻辑交给代理
  • 可扩展:可以不断叠加新的代理增强

7.2 代理模式缺点

  • 增加复杂度:多一层对象、多一段调用链
  • 可能影响性能:动态代理依赖反射/拦截
  • 不当使用会变"滥用中间层":过度代理导致调试困难

7.3 和装饰器模式的简单区分

  • 装饰器:通常强调"功能叠加"(也会包一层对象,但更偏向对象行为扩展)
  • 代理:更强调"控制访问/代表/访问管理"(例如权限、远程、延迟)

在很多框架实现里,两者可能"看起来很像",但意图和侧重点不同。


8. 总结

代理模式就是:
不直接调用真实对象,而是先走一层"代表对象",在这里完成访问控制与增强,然后再把请求转发给真实对象。

相关推荐
不像程序员的程序媛2 小时前
es查询是否存在某个字段
java·前端·elasticsearch
两年半的个人练习生^_^2 小时前
ThreadLocal的使用和源码
java·开发语言
SarL EMEN2 小时前
Spring boot创建时常用的依赖
java·spring boot·后端
随风,奔跑2 小时前
Spring Data Redis
java·redis·spring
weixin_408099672 小时前
文字识别通用OCR接口调用与功能说明
图像处理·人工智能·后端·python·ocr·api·文字识别
roamingcode2 小时前
应对 Codex 0.118.0 破坏性更新:Slash Prompt Router 架构解析与实践
java·开发语言·prompt·codex·skill
计算机学姐2 小时前
基于SpringBoot的特色美食分享系统
java·vue.js·spring boot·后端·spring·tomcat·mybatis
砍光二叉树2 小时前
【设计模式】行为型-访问者模式
设计模式·访问者模式
砍光二叉树2 小时前
【设计模式】行为型-状态模式
设计模式·状态模式