Skip to content

代理模式(Proxy Pattern) ​

一、模式概述 ​

代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象和目标对象实现相同的接口,客户端通过代理对象间接访问目标对象。

核心思想:在不改变目标对象的前提下,提供额外的功能(如权限控制、日志记录、缓存等),通过代理来增强或控制对目标对象的访问。

二、代理的类型 ​

代理类型说明典型应用
静态代理编译时确定代理类简单的增强逻辑
JDK 动态代理基于接口,运行时生成代理类Spring AOP 默认方式
CGLIB 动态代理基于继承,运行时生成子类Spring AOP(无接口时)
远程代理为远程对象提供本地代理RPC 框架
虚拟代理延迟加载大对象图片懒加载
保护代理控制对象的访问权限权限校验

三、示例1:静态代理 ​

问题场景 ​

对用户服务添加日志记录和性能监控功能。

java
// ========== 公共接口 ==========

/**
 * 用户服务接口
 */
interface UserService {
    void addUser(String name);
    String findUser(int id);
    void deleteUser(int id);
}

// ========== 目标类(真实对象) ==========

/**
 * 用户服务实现
 */
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("  [服务] 添加用户: " + name);
        // 模拟业务逻辑
        try { Thread.sleep(100); } catch (InterruptedException e) { }
    }

    @Override
    public String findUser(int id) {
        System.out.println("  [服务] 查找用户, ID: " + id);
        try { Thread.sleep(50); } catch (InterruptedException e) { }
        return "User-" + id;
    }

    @Override
    public void deleteUser(int id) {
        System.out.println("  [服务] 删除用户, ID: " + id);
    }
}

// ========== 静态代理类 ==========

/**
 * 用户服务静态代理
 * 在不修改目标类的情况下,添加日志和性能监控功能
 */
class UserServiceProxy implements UserService {
    private final UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void addUser(String name) {
        System.out.println("[代理-前置] 调用 addUser, 参数: " + name);
        long start = System.currentTimeMillis();

        target.addUser(name);  // 调用目标对象的方法

        long cost = System.currentTimeMillis() - start;
        System.out.println("[代理-后置] addUser 执行完成, 耗时: " + cost + "ms");
    }

    @Override
    public String findUser(int id) {
        System.out.println("[代理-前置] 调用 findUser, 参数: " + id);
        long start = System.currentTimeMillis();

        String result = target.findUser(id);  // 调用目标对象的方法

        long cost = System.currentTimeMillis() - start;
        System.out.println("[代理-后置] findUser 执行完成, 耗时: " + cost + "ms");
        return result;
    }

    @Override
    public void deleteUser(int id) {
        // 权限检查(代理特有的功能)
        System.out.println("[代理-权限] 检查删除权限...");
        boolean hasPermission = true; // 模拟权限检查
        if (!hasPermission) {
            System.out.println("[代理-权限] 权限不足,拒绝删除!");
            return;
        }

        System.out.println("[代理-前置] 调用 deleteUser, 参数: " + id);
        target.deleteUser(id);
        System.out.println("[代理-后置] deleteUser 执行完成");
    }
}

// ========== 客户端代码 ==========

public class StaticProxyDemo {
    public static void main(String[] args) {
        // 创建代理对象
        UserService proxy = new UserServiceProxy(new UserServiceImpl());

        System.out.println("===== 添加用户 =====");
        proxy.addUser("张三");

        System.out.println("\n===== 查找用户 =====");
        String user = proxy.findUser(1001);
        System.out.println("  结果: " + user);

        System.out.println("\n===== 删除用户 =====");
        proxy.deleteUser(1001);
    }
}

四、示例2:JDK 动态代理 ​

原理 ​

JDK 动态代理基于 java.lang.reflect.Proxy 和 InvocationHandler 接口,在运行时动态生成代理类。

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

// ========== 公共接口 ==========

/**
 * 支付服务接口
 */
interface PaymentService {
    boolean pay(double amount, String orderId);
    boolean refund(double amount, String orderId);
}

// ========== 目标类 ==========

/**
 * 支付服务实现
 */
class AlipayService implements PaymentService {
    @Override
    public boolean pay(double amount, String orderId) {
        System.out.println("  [支付宝] 支付 " + amount + " 元,订单号: " + orderId);
        return true;
    }

    @Override
    public boolean refund(double amount, String orderId) {
        System.out.println("  [支付宝] 退款 " + amount + " 元,订单号: " + orderId);
        return true;
    }
}

// ========== 动态代理处理器 ==========

/**
 * 通用的日志代理处理器
 * 通过反射机制拦截方法调用,动态添加增强逻辑
 */
class LogInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        System.out.println("[动态代理-前置] 调用方法: " + methodName
                + ", 参数: " + java.util.Arrays.toString(args));

        long start = System.nanoTime();

        // 通过反射调用目标对象的真实方法
        Object result = method.invoke(target, args);

        long cost = (System.nanoTime() - start) / 1000;
        System.out.println("[动态代理-后置] 方法: " + methodName
                + ", 返回值: " + result + ", 耗时: " + cost + "μs");

        return result;
    }
}

// ========== 安全代理处理器 ==========

/**
 * 安全代理处理器
 * 只允许特定方法被调用
 */
class SecurityInvocationHandler implements InvocationHandler {
    private final Object target;
    private final java.util.Set<String> allowedMethods;

    public SecurityInvocationHandler(Object target, String... allowedMethods) {
        this.target = target;
        this.allowedMethods = new java.util.HashSet<>(java.util.Arrays.asList(allowedMethods));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!allowedMethods.contains(method.getName())) {
            System.out.println("[安全代理] 方法 " + method.getName() + " 未授权,拒绝访问!");
            return null;
        }
        System.out.println("[安全代理] 方法 " + method.getName() + " 已授权,允许执行");
        return method.invoke(target, args);
    }
}

// ========== 客户端代码 ==========

public class DynamicProxyDemo {
    /**
     * 创建动态代理对象的工具方法
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
    }

    public static void main(String[] args) {
        AlipayService realService = new AlipayService();

        // 1. 创建日志代理
        System.out.println("===== 日志代理 =====");
        PaymentService logProxy = createProxy(realService, new LogInvocationHandler(realService));
        logProxy.pay(99.9, "ORDER001");
        System.out.println();
        logProxy.refund(30.0, "ORDER001");

        System.out.println();

        // 2. 创建安全代理(只允许 pay 方法)
        System.out.println("===== 安全代理 =====");
        PaymentService securityProxy = createProxy(realService,
                new SecurityInvocationHandler(realService, "pay"));
        securityProxy.pay(50.0, "ORDER002");    // 允许
        System.out.println();
        securityProxy.refund(50.0, "ORDER002");  // 拒绝

        System.out.println();

        // 3. 链式代理(日志 + 安全)
        System.out.println("===== 链式代理(日志 + 安全) =====");
        PaymentService chainedProxy = createProxy(realService,
                new LogInvocationHandler(
                    createProxy(realService,
                        new SecurityInvocationHandler(realService, "pay", "refund"))
                ));
        chainedProxy.pay(200.0, "ORDER003");
    }
}

五、示例3:CGLIB 动态代理 ​

原理 ​

CGLIB(Code Generation Library)通过生成目标类的子类来实现代理,不需要目标类实现接口。

java
// 注意:CGLIB 需要引入 cglib 依赖
// <dependency>
//     <groupId>cglib</groupId>
//     <artifactId>cglib</artifactId>
//     <version>3.3.0</version>
// </dependency>

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 订单服务(没有实现接口)
 */
class OrderService {
    public String createOrder(String product, int quantity) {
        System.out.println("  [订单服务] 创建订单: " + product + " x " + quantity);
        return "ORD" + System.currentTimeMillis();
    }

    public void cancelOrder(String orderId) {
        System.out.println("  [订单服务] 取消订单: " + orderId);
    }
}

/**
 * CGLIB 动态代理处理器
 */
class OrderMethodInterceptor implements MethodInterceptor {
    private final Object target;

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

    @Override
    public Object intercept(Object obj, java.lang.reflect.Method method,
                            Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();

        System.out.println("[CGLIB代理-前置] 调用: " + methodName
                + ", 参数: " + java.util.Arrays.toString(args));

        // 使用 methodProxy.invokeSuper 调用父类(目标类)的方法,性能更好
        Object result = methodProxy.invokeSuper(obj, args);

        System.out.println("[CGLIB代理-后置] 调用: " + methodName
                + ", 返回: " + result);

        return result;
    }
}

/**
 * CGLIB 代理工厂
 */
class CglibProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new OrderMethodInterceptor(target));
        return (T) enhancer.create();
    }
}

// ========== 客户端代码 ==========

public class CglibProxyDemo {
    public static void main(String[] args) {
        // CGLIB 不需要接口,直接代理类
        OrderService proxy = CglibProxyFactory.createProxy(new OrderService());

        System.out.println("===== 创建订单 =====");
        String orderId = proxy.createOrder("iPhone 16", 1);
        System.out.println("  订单号: " + orderId);

        System.out.println("\n===== 取消订单 =====");
        proxy.cancelOrder(orderId);
    }
}

六、三种代理方式对比 ​

特性静态代理JDK 动态代理CGLIB 动态代理
实现方式手动编写代理类基于反射,运行时生成基于继承,运行时生成子类
接口要求必须实现相同接口必须基于接口不需要接口,代理类的子类
性能编译时确定,性能最好反射调用,性能一般字节码生成,性能较好
灵活性低(每个接口都要写代理类)高(一个处理器代理所有接口)高(代理所有类)
Spring AOP不使用默认方式(有接口时)无接口时使用
适用场景简单场景,代理逻辑固定通用的 AOP 增强无接口的类的代理

代码结构对比 ​

静态代理:
  Interface ──implements──▶ RealClass
  Interface ──implements──▶ ProxyClass (持有RealClass引用)

JDK 动态代理:
  Interface ──implements──▶ RealClass
  Proxy.newProxyInstance() ──生成──▶ $Proxy0 (运行时生成,实现接口)

CGLIB 动态代理:
  RealClass
  Enhancer.create() ──生成──▶ RealClass$$EnhancerByCGLIB (运行时生成,继承目标类)

七、UML 类图 ​

┌────────────┐         ┌────────────────┐
│   Client    │───────▶│  Subject (接口) │
└────────────┘        └────────┬───────┘
                              │ 实现
                    ┌─────────┴────────┐
                    │                  │
             ┌──────┴──────┐   ┌──────┴──────┐
             │RealSubject  │   │   Proxy     │
             │(真实对象)    │   │(代理对象)    │
             │+request()   │   │-realSubject │
             └─────────────┘   │+request()   │
                               │ +预处理()    │
                               │ +real.request()│
                               │ +后处理()    │
                               └─────────────┘

八、Spring AOP 中的代理模式 ​

java
/**
 * Spring AOP 底层就是代理模式的典型应用
 *
 * @Component
 * @Aspect
 */
public class UserServiceAspect {

    /**
     * 前置通知:在方法执行前执行
     */
    // @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("[AOP前置] " + joinPoint.getSignature().getName());
    }

    /**
     * 后置通知:在方法执行后执行
     */
    // @After("execution(* com.example.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("[AOP后置] " + joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知:完全控制方法的执行
     */
    // @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[AOP环绕-前] " + pjp.getSignature().getName());
        Object result = pjp.proceed();  // 调用目标方法
        System.out.println("[AOP环绕-后] " + pjp.getSignature().getName());
        return result;
    }
}

/**
 * Spring 选择代理方式的逻辑:
 *
 * if (目标类实现了接口) {
 *     使用 JDK 动态代理  // 默认
 * } else {
 *     使用 CGLIB 动态代理
 * }
 *
 * // Spring Boot 2.x 默认使用 CGLIB
 * // 可通过配置切换:
 * spring.aop.proxy-target-class=false  // 强制使用 JDK 动态代理
 */

九、实际应用场景 ​

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

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 + " (耗时操作)");
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
    }

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

class VirtualImageProxy implements Image {
    private RealImage realImage;
    private String filename;

    public VirtualImageProxy(String filename) {
        this.filename = filename;
        // 构造时不加载图片
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);  // 第一次使用时才加载
        }
        realImage.display();
    }
}

2. 权限控制(保护代理) ​

java
/**
 * 数据访问保护代理
 * 根据用户角色控制数据的读写权限
 */
interface DataAccess {
    String readData(String key);
    void writeData(String key, String value);
}

class FileDataAccess implements DataAccess {
    private java.util.Map<String, String> data = new java.util.HashMap<>();

    @Override
    public String readData(String key) {
        return data.getOrDefault(key, "无数据");
    }

    @Override
    public void writeData(String key, String value) {
        data.put(key, value);
        System.out.println("  [文件] 写入: " + key + " = " + value);
    }
}

class DataAccessProxy implements DataAccess {
    private final DataAccess target;
    private final String userRole;

    public DataAccessProxy(DataAccess target, String userRole) {
        this.target = target;
        this.userRole = userRole;
    }

    @Override
    public String readData(String key) {
        System.out.println("[权限代理] 检查读取权限...");
        if ("admin".equals(userRole) || "user".equals(userRole)) {
            return target.readData(key);
        }
        throw new SecurityException("无读取权限!");
    }

    @Override
    public void writeData(String key, String value) {
        System.out.println("[权限代理] 检查写入权限...");
        if ("admin".equals(userRole)) {
            target.writeData(key, value);
        } else {
            throw new SecurityException("无写入权限!需要 admin 角色。");
        }
    }
}

十、优缺点分析 ​

优点 ​

  1. 职责清晰:将核心业务逻辑与横切关注点(日志、安全、缓存)分离
  2. 开闭原则:增加新的代理功能不需要修改目标类
  3. 灵活性强:可以在不修改目标类的情况下动态添加功能
  4. 保护目标对象:客户端不直接访问目标对象,增加了安全性

缺点 ​

  1. 增加系统复杂度:引入了代理类,增加了系统的类数量
  2. 性能开销:代理层会带来额外的方法调用开销
  3. 静态代理不够灵活:每个接口都需要单独编写代理类

十一、使用场景 ​

  • 远程代理:为远程对象提供本地代理(如 RPC)
  • 虚拟代理:延迟创建开销大的对象
  • 保护代理:控制对象的访问权限
  • 日志/监控代理:记录方法调用日志
  • 缓存代理:缓存方法结果,避免重复计算
  • 事务代理:Spring 声明式事务
  • AOP:面向切面编程的核心实现

十二、与其他模式的关系 ​

  • 装饰器模式 vs 代理模式:装饰器模式关注为对象添加功能,代理模式关注控制对象访问
  • 适配器模式 vs 代理模式:适配器改变接口,代理保持接口不变
  • 外观模式 vs 代理模式:外观简化接口,代理保持接口但控制访问

总结:代理模式是 Java 中最常用的设计模式之一,尤其在 Spring 框架中广泛应用。选择哪种代理方式取决于是否需要接口、性能要求和项目规范。静态代理适合简单场景,JDK 动态代理适合基于接口的通用增强,CGLIB 适合无接口的类。