【设计模式】结构型-代理模式

文章目录

  • 前言
  • 一、概念
  • 二、核心思想
  • 三、Java代码实现
    • [1. 定义抽象主题(用户服务接口)](#1. 定义抽象主题(用户服务接口))
    • [2. 定义真实主题(用户服务实现类)](#2. 定义真实主题(用户服务实现类))
    • [3. 静态代理(手动编写代理类,增强逻辑固定)](#3. 静态代理(手动编写代理类,增强逻辑固定))
      • [3.1 静态代理核心说明](#3.1 静态代理核心说明)
      • [3.2 静态代理代码实现](#3.2 静态代理代码实现)
    • [4. JDK动态代理(基于接口,增强逻辑可复用)](#4. JDK动态代理(基于接口,增强逻辑可复用))
      • [4.1 JDK动态代理核心说明](#4.1 JDK动态代理核心说明)
      • [4.2 JDK动态代理代码实现](#4.2 JDK动态代理代码实现)
    • [5. CGLIB动态代理(适配无接口的类)](#5. CGLIB动态代理(适配无接口的类))
      • [5.1 CGLIB动态代理核心说明](#5.1 CGLIB动态代理核心说明)
      • [5.2 CGLIB动态代理代码实现](#5.2 CGLIB动态代理代码实现)
    • [6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理)](#6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理))
  • 四、三种代理方式核心对比
  • 五、优缺点
    • [1. 优点](#1. 优点)
    • [2. 缺点](#2. 缺点)
  • 六、应用场景
  • 七、注意事项
  • 总结

前言

在AI时代,代码的编写可以被大模型辅助甚至替代,但程序员真正的核心竞争力是技术思维------设计模式这类沉淀了数十年的"内功心法",决定了代码的可维护性、扩展性和稳定性,是AI无法完全替代的核心能力。代理模式作为结构型模式的核心成员,专注于"控制对象访问、增强对象行为",解决了直接访问对象带来的权限管控、性能损耗、功能缺失等问题,是企业级开发中管控对象访问的核心范式。

一、概念

代理模式(Proxy Pattern)是一种结构型设计模式,核心目标是为其他对象提供一个代理以控制对这个对象的访问,同时可在访问前后添加额外的逻辑(如权限校验、日志记录、缓存、延迟加载)。简单来说,代理就像生活中的"经纪人"------明星(目标对象)不直接对接粉丝/商家,而是由经纪人(代理)处理预约、谈判、行程安排等事务,明星只需专注于表演核心功能,经纪人则负责管控访问和额外事务。

在编程中,当需要对一个对象的访问进行控制(如权限校验)、增强其行为(如添加日志),或延迟其创建(如懒加载)时,代理模式是最优选择------代理对象与目标对象实现相同接口,客户端通过代理访问目标对象,既不改变目标对象的核心逻辑,又能灵活添加管控和增强逻辑。

二、核心思想

  1. 抽象主题(Subject):定义目标对象和代理对象的共同接口,规范核心业务方法,保证客户端能以相同方式调用目标对象和代理对象;
  2. 真实主题(Real Subject):实现抽象主题接口,是真正执行业务逻辑的目标对象,提供核心功能;
  3. 代理(Proxy):实现抽象主题接口,同时持有真实主题的引用,在调用真实主题方法的前后添加额外逻辑(如权限校验、日志),并控制对真实主题的访问;
  4. 客户端(Client):仅依赖抽象主题接口,通过代理对象间接访问真实主题,无需知道真实主题的存在。

代理模式的核心本质是控制访问、增强行为 ------通过代理封装真实主题,既管控了对真实主题的访问权限,又能在不修改真实主题代码的前提下增强其行为,符合"开闭原则"和"单一职责原则"。

三、Java代码实现

以"用户服务接口"场景为例:系统中有UserService接口(包含查询用户、修改用户信息功能),UserServiceImpl是真实实现类。需通过代理模式实现:① 访问权限校验(仅管理员可修改用户信息);② 日志记录(记录接口调用时间和参数);③ 延迟加载(真实对象仅在首次调用时创建)。

1. 定义抽象主题(用户服务接口)

java 复制代码
/**
 * 抽象主题:用户服务接口
 * 定义核心业务方法,是代理和真实主题的共同接口
 */
public interface UserService {
    // 查询用户信息
    String getUserInfo(String userId);
    // 修改用户信息
    boolean updateUserInfo(String userId, String newInfo);
}

2. 定义真实主题(用户服务实现类)

java 复制代码
/**
 * 真实主题:用户服务实现类
 * 实现核心业务逻辑,仅关注功能本身,无额外管控逻辑
 */
public class UserServiceImpl implements UserService {
    // 模拟数据库存储用户信息
    private final static Map<String, String> USER_DATA = new HashMap<>();

    // 静态初始化测试数据
    static {
        USER_DATA.put("001", "张三,普通用户");
        USER_DATA.put("002", "李四,管理员");
    }

    @Override
    public String getUserInfo(String userId) {
        // 核心业务:查询用户信息
        System.out.println("【真实主题】查询用户" + userId + "信息");
        return USER_DATA.getOrDefault(userId, "用户不存在");
    }

    @Override
    public boolean updateUserInfo(String userId, String newInfo) {
        // 核心业务:修改用户信息
        System.out.println("【真实主题】修改用户" + userId + "信息为:" + newInfo);
        USER_DATA.put(userId, newInfo);
        return true;
    }
}

3. 静态代理(手动编写代理类,增强逻辑固定)

3.1 静态代理核心说明

静态代理是编译期确定代理类与目标类关系的实现方式,开发者需手动编写与目标类实现相同接口的代理类,在代理类中硬编码增强逻辑(如权限、日志),并持有目标类引用。其优点是实现简单、调试方便、无运行时性能损耗;缺点是类数量易膨胀,目标接口变更时代理类需同步修改,增强逻辑复用性差。

3.2 静态代理代码实现

java 复制代码
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 静态代理:用户服务代理类
 * 手动编写代理类,实现权限校验、日志记录、延迟加载逻辑
 */
public class UserServiceStaticProxy implements UserService {
    // 持有真实主题引用(延迟加载:初始为null,首次调用时创建)
    private UserService userService;

    // 模拟当前登录用户的角色(实际开发中从上下文获取)
    private String currentUserRole;

    public UserServiceStaticProxy(String currentUserRole) {
        this.currentUserRole = currentUserRole;
    }

    // 延迟加载真实主题(首次调用时创建)
    private UserService getRealSubject() {
        if (userService == null) {
            System.out.println("【静态代理】首次调用,创建真实主题实例");
            userService = new UserServiceImpl();
        }
        return userService;
    }

    @Override
    public String getUserInfo(String userId) {
        // 增强逻辑1:日志记录(调用前)
        System.out.println("【静态代理】调用getUserInfo,参数:" + userId + ",时间:" + LocalDateTime.now());
        // 调用真实主题方法
        String result = getRealSubject().getUserInfo(userId);
        // 增强逻辑2:日志记录(调用后)
        System.out.println("【静态代理】getUserInfo调用完成,结果:" + result);
        return result;
    }

    @Override
    public boolean updateUserInfo(String userId, String newInfo) {
        // 增强逻辑1:权限校验(调用前)
        if (!"管理员".equals(currentUserRole)) {
            System.out.println("【静态代理】权限校验失败:非管理员禁止修改用户信息");
            return false;
        }
        // 增强逻辑2:日志记录(调用前)
        System.out.println("【静态代理】调用updateUserInfo,参数:" + userId + "," + newInfo + ",时间:" + LocalDateTime.now());
        // 调用真实主题方法
        boolean result = getRealSubject().updateUserInfo(userId, newInfo);
        // 增强逻辑3:日志记录(调用后)
        System.out.println("【静态代理】updateUserInfo调用完成,结果:" + result);
        return result;
    }
}

4. JDK动态代理(基于接口,增强逻辑可复用)

4.1 JDK动态代理核心说明

JDK动态代理是JDK原生支持、运行时动态生成代理类 的实现方式,基于java.lang.reflect.ProxyInvocationHandler实现。核心要求:目标类必须实现至少一个接口,代理类会动态实现与目标类相同的接口,通过反射调用目标方法。其优点是无需手动编写代理类、增强逻辑可复用;缺点是目标类必须实现接口,存在轻微反射性能损耗。

4.2 JDK动态代理代码实现

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 动态代理:InvocationHandler实现类
 * 定义通用的增强逻辑(权限、日志),可适配任意接口,无需手动编写代理类
 */
public class UserServiceDynamicProxy implements InvocationHandler {
    // 持有真实主题对象(任意类型)
    private Object target;
    // 当前用户角色
    private String currentUserRole;

    public UserServiceDynamicProxy(Object target, String currentUserRole) {
        this.target = target;
        this.currentUserRole = currentUserRole;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        // 获取方法名,区分不同业务方法
        String methodName = method.getName();

        try {
            // 增强逻辑1:日志记录(调用前)
            System.out.println("【动态代理】调用" + methodName + ",参数:" + Arrays.toString(args) + ",时间:" + LocalDateTime.now());

            // 增强逻辑2:权限校验(仅updateUserInfo方法需要)
            if ("updateUserInfo".equals(methodName) && !"管理员".equals(currentUserRole)) {
                System.out.println("【动态代理】权限校验失败:非管理员禁止修改用户信息");
                return false;
            }

            // 调用真实主题的方法(核心:通过反射调用)
            result = method.invoke(target, args);

            // 增强逻辑3:日志记录(调用后)
            System.out.println("【动态代理】" + methodName + "调用完成,结果:" + result);
        } catch (Exception e) {
            // 增强逻辑4:异常处理
            System.out.println("【动态代理】调用" + methodName + "异常:" + e.getMessage());
            throw e;
        }

        return result;
    }

    // 生成代理对象的工具方法
    public static Object createProxy(Object target, String currentUserRole) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 类加载器:与目标类保持一致
                target.getClass().getInterfaces(), // 目标类实现的接口:代理类会实现这些接口
                new UserServiceDynamicProxy(target, currentUserRole) // 调用处理器:封装增强逻辑
        );
    }
}

5. CGLIB动态代理(适配无接口的类)

5.1 CGLIB动态代理核心说明

CGLIB(Code Generation Library)是基于ASM字节码生成框架的动态代理方式,无需目标类实现接口,通过在运行时生成目标类的子类作为代理类,重写目标类的非final方法实现增强。其优点是适配范围更广(支持无接口类)、调用效率略高于JDK动态代理;缺点是需引入CGLIB依赖,无法代理final类和final方法。

5.2 CGLIB动态代理代码实现

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * CGLIB动态代理:适配无实现接口的类(需引入cglib依赖)
 * 核心:继承目标类,生成子类作为代理对象,重写非final方法
 */
public class UserServiceCglibProxy implements MethodInterceptor {
    // 目标对象
    private Object target;
    // 当前用户角色
    private String currentUserRole;

    public UserServiceCglibProxy(Object target, String currentUserRole) {
        this.target = target;
        this.currentUserRole = currentUserRole;
    }

    // 生成CGLIB代理对象
    public Object createProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 设置父类为目标类(核心:生成子类)
        enhancer.setCallback(this); // 设置回调函数:增强逻辑写在intercept方法中
        return enhancer.create(); // 创建代理对象(目标类的子类)
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result = null;
        String methodName = method.getName();

        try {
            // 增强逻辑:日志+权限校验(同JDK动态代理)
            System.out.println("【CGLIB代理】调用" + methodName + ",参数:" + Arrays.toString(args) + ",时间:" + LocalDateTime.now());
            if ("updateUserInfo".equals(methodName) && !"管理员".equals(currentUserRole)) {
                System.out.println("【CGLIB代理】权限校验失败:非管理员禁止修改用户信息");
                return false;
            }
            // 调用目标方法(核心:通过MethodProxy调用父类方法,避免反射损耗)
            result = proxy.invokeSuper(obj, args);
            System.out.println("【CGLIB代理】" + methodName + "调用完成,结果:" + result);
        } catch (Exception e) {
            System.out.println("【CGLIB代理】调用" + methodName + "异常:" + e.getMessage());
            throw e;
        }

        return result;
    }
}

// CGLIB依赖(Maven)
// <dependency>
//     <groupId>cglib</groupId>
//     <artifactId>cglib-nodep</artifactId>
//     <version>3.3.0</version>
// </dependency>

6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理)

java 复制代码
/**
 * 客户端:用户服务调用层
 * 分别测试静态代理、JDK动态代理、CGLIB动态代理的使用
 */
public class Client {
    public static void main(String[] args) {
        // ====================== 测试静态代理 ======================
        System.out.println("=== 静态代理测试(普通用户) ===");
        UserService staticProxyForNormal = new UserServiceStaticProxy("普通用户");
        // 普通用户查询用户信息(允许)
        System.out.println(staticProxyForNormal.getUserInfo("001"));
        // 普通用户修改用户信息(禁止)
        staticProxyForNormal.updateUserInfo("001", "张三,VIP用户");

        System.out.println("\n=== 静态代理测试(管理员) ===");
        UserService staticProxyForAdmin = new UserServiceStaticProxy("管理员");
        // 管理员修改用户信息(允许)
        staticProxyForAdmin.updateUserInfo("001", "张三,VIP用户");
        // 管理员查询修改后的信息
        System.out.println(staticProxyForAdmin.getUserInfo("001"));

        // ====================== 测试JDK动态代理 ======================
        System.out.println("\n=== JDK动态代理测试(普通用户) ===");
        UserService realService = new UserServiceImpl();
        // 创建JDK动态代理对象(需传入实现接口的目标对象)
        UserService dynamicProxyForNormal = (UserService) UserServiceDynamicProxy.createProxy(realService, "普通用户");
        // 普通用户查询用户信息(允许)
        System.out.println(dynamicProxyForNormal.getUserInfo("002"));
        // 普通用户修改用户信息(禁止)
        dynamicProxyForNormal.updateUserInfo("002", "李四,超级管理员");

        System.out.println("\n=== JDK动态代理测试(管理员) ===");
        UserService dynamicProxyForAdmin = (UserService) UserServiceDynamicProxy.createProxy(realService, "管理员");
        // 管理员修改用户信息(允许)
        dynamicProxyForAdmin.updateUserInfo("002", "李四,超级管理员");
        // 管理员查询修改后的信息
        System.out.println(dynamicProxyForAdmin.getUserInfo("002"));

        // ====================== 测试CGLIB动态代理 ======================
        System.out.println("\n=== CGLIB动态代理测试(管理员) ===");
        // 目标类无需实现接口(此处用实现接口的类仅为演示,实际可用于无接口类)
        UserServiceImpl realServiceForCglib = new UserServiceImpl();
        UserServiceCglibProxy cglibProxy = new UserServiceCglibProxy(realServiceForCglib, "管理员");
        // 创建CGLIB代理对象(目标类的子类)
        UserServiceImpl proxyService = (UserServiceImpl) cglibProxy.createProxy();
        // 调用代理方法
        proxyService.updateUserInfo("003", "王五,新用户");
        System.out.println(proxyService.getUserInfo("003"));
    }
}

输出结果:

复制代码
=== 静态代理测试(普通用户) ===
【静态代理】调用getUserInfo,参数:001,时间:2026-03-23T15:30:00
【静态代理】首次调用,创建真实主题实例
【真实主题】查询用户001信息
【静态代理】getUserInfo调用完成,结果:张三,普通用户
张三,普通用户
【静态代理】调用updateUserInfo,参数:001,张三,VIP用户,时间:2026-03-23T15:30:00
【静态代理】权限校验失败:非管理员禁止修改用户信息
false

=== 静态代理测试(管理员) ===
【静态代理】调用updateUserInfo,参数:001,张三,VIP用户,时间:2026-03-23T15:30:00
【真实主题】修改用户001信息为:张三,VIP用户
【静态代理】updateUserInfo调用完成,结果:true
【静态代理】调用getUserInfo,参数:001,时间:2026-03-23T15:30:00
【真实主题】查询用户001信息
【静态代理】getUserInfo调用完成,结果:张三,VIP用户
张三,VIP用户

=== JDK动态代理测试(普通用户) ===
【动态代理】调用getUserInfo,参数:[002],时间:2026-03-23T15:30:00
【真实主题】查询用户002信息
【动态代理】getUserInfo调用完成,结果:李四,管理员
李四,管理员
【动态代理】调用updateUserInfo,参数:[002, 李四,超级管理员],时间:2026-03-23T15:30:00
【动态代理】权限校验失败:非管理员禁止修改用户信息
false

=== JDK动态代理测试(管理员) ===
【动态代理】调用updateUserInfo,参数:[002, 李四,超级管理员],时间:2026-03-23T15:30:00
【真实主题】修改用户002信息为:李四,超级管理员
【动态代理】updateUserInfo调用完成,结果:true
【动态代理】调用getUserInfo,参数:[002],时间:2026-03-23T15:30:00
【真实主题】查询用户002信息
【动态代理】getUserInfo调用完成,结果:李四,超级管理员
李四,超级管理员

=== CGLIB动态代理测试(管理员) ===
【CGLIB代理】调用updateUserInfo,参数:[003, 王五,新用户],时间:2026-03-23T15:30:00
【真实主题】修改用户003信息为:王五,新用户
【CGLIB代理】updateUserInfo调用完成,结果:true
【CGLIB代理】调用getUserInfo,参数:[003],时间:2026-03-23T15:30:00
【真实主题】查询用户003信息
【CGLIB代理】getUserInfo调用完成,结果:王五,新用户
王五,新用户

四、三种代理方式核心对比

维度 静态代理 JDK动态代理 CGLIB动态代理
依赖条件 目标类必须实现接口 需引入CGLIB依赖,目标类非final
实现原理 手动编写代理类,实现与目标类相同接口 运行时动态生成接口实现类,基于反射调用 运行时生成目标类子类,基于字节码调用
增强逻辑编写 硬编码到代理类的每个方法 统一写在InvocationHandler.invoke方法 统一写在MethodInterceptor.intercept方法
性能 无损耗(编译期确定) 轻微反射损耗(JDK8+已优化) 字节码生成,损耗略低于JDK动态代理
维护成本 高(接口变更需同步修改) 低(通用逻辑可复用) 低(通用逻辑可复用)
适用场景 目标类少、逻辑固定 目标类实现接口的场景 目标类无接口、需代理非接口方法的场景

五、优缺点

1. 优点

  1. 控制访问:可在调用目标对象前添加权限校验、限流、熔断等逻辑,管控对目标对象的访问;
  2. 增强行为:可在调用前后添加日志、缓存、事务、监控等逻辑,增强目标对象功能,且无需修改目标对象代码;
  3. 延迟加载:可延迟创建目标对象(如静态代理中的懒加载),减少系统初始化时的资源消耗;
  4. 解耦客户端与目标对象:客户端仅依赖抽象接口,代理层封装了目标对象的访问逻辑,降低耦合度;
  5. 动态代理灵活性高:JDK/CGLIB动态代理可适配任意类/接口,无需手动编写大量静态代理类。

2. 缺点

  1. 静态代理类数量膨胀:每新增一个接口,需手动编写对应的静态代理类,类数量增多,维护成本高;
  2. 动态代理学习成本高:JDK/CGLIB动态代理涉及反射、字节码生成,理解和调试难度高于静态代理;
  3. 性能损耗:动态代理基于反射/字节码生成,调用效率略低于静态代理和直接调用(但日常开发中可忽略);
  4. CGLIB限制:CGLIB代理无法代理final类和final方法(因为无法生成子类)。

六、应用场景

代理模式适用于需控制对象访问、增强对象行为、延迟对象创建的场景:

  1. 权限管控:如接口鉴权、数据权限控制(仅允许特定角色访问/修改数据);
  2. 日志/监控:如记录接口调用日志、统计接口响应时间、埋点监控;
  3. 缓存增强:如给查询接口添加缓存,避免重复查询数据库;
  4. 延迟加载:如MyBatis的Mapper代理(延迟创建Mapper实例)、Hibernate的懒加载(延迟加载关联对象);
  5. 远程代理:如RPC框架(Dubbo、Spring Cloud)的远程代理,客户端通过代理调用远程服务,代理封装网络通信逻辑;
  6. 框架中的应用:Spring AOP的底层实现(JDK动态代理+CGLIB动态代理)、MyBatis的Mapper代理、Spring的事务代理。

七、注意事项

  1. 区分代理模式与装饰器模式:代理模式关注"控制访问"(如权限、延迟加载),装饰器模式关注"增强功能"(如添加配料);代理通常持有一个目标对象,装饰器可嵌套多个;
  2. 选择合适的代理方式:有接口时优先用JDK动态代理,无接口时用CGLIB代理,简单场景用静态代理;
  3. 避免过度代理:简单接口(无管控/增强需求)无需使用代理,直接调用目标对象更简洁;
  4. 动态代理的性能优化:高频调用的接口可使用静态代理或缓存动态代理对象,减少反射开销。

总结

  1. 代理模式核心是控制对象访问、增强对象行为,通过代理封装目标对象,在不修改目标对象代码的前提下实现管控和增强;
  2. 静态代理简单易实现但类数量膨胀,JDK动态代理依赖接口、基于反射,CGLIB动态代理无接口限制、基于字节码生成,需根据场景选择;
  3. 三种代理方式核心差异在于实现原理和依赖条件:静态代理编译期确定,JDK动态代理依赖接口,CGLIB动态代理依赖字节码生成;
  4. Spring AOP是代理模式的经典应用(底层基于JDK/CGLIB动态代理),理解代理模式是掌握Spring AOP的关键。
相关推荐
新缸中之脑4 小时前
AI智能体五大设计模式
人工智能·机器学习·设计模式
砍光二叉树5 小时前
【设计模式】结构型-装饰器模式
设计模式·装饰器模式
han_5 小时前
JavaScript设计模式(三):代理模式实现与应用
前端·javascript·设计模式
我的offer在哪里5 小时前
POM 设计模式深度解析|博客视角:从原理到落地,让自动化测试脚本 “活” 起来
设计模式
程序员Terry5 小时前
Java 代理模式:从生活中的"中介"到代码中的"代理人"
后端·设计模式
砍光二叉树5 小时前
【设计模式】结构型-适配器模式
设计模式·适配器模式
ambition202425 小时前
动态规划解最长不下降子序列:深入理解状态转移与内层循环
代理模式
Yu_Lijing6 小时前
基于C++的《Head First设计模式》笔记——蝇量模式
c++·笔记·设计模式
敲代码的约德尔人1 天前
JavaScript 设计模式完全指南
javascript·设计模式