前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、代理模式(Proxy Pattern)

1. 模式概述

代理模式 是一种 结构型设计模式,它为其他对象提供一个 代理或占位符,以控制对这个对象的访问。

核心思想“我不直接见你,先让代理人处理。”

代理模式 = 中介 + 增强 + 控制

  • 它不是“多此一举”,而是“安全可控的封装”。

类比理解:

  • 房产中介:你不能直接联系房东,需通过中介看房、谈价;
  • 公司前台:访客不能直接进办公室,需登记并由前台通知;
  • 网络代理服务器:客户端不直连目标服务器,而是通过代理中转。

2. 使用场景

代理模式适用于以下情况:

  • 控制访问权限(如远程、安全、智能引用);
  • 延迟初始化(创建开销大的对象时按需加载);
  • 添加日志、缓存、事务等横切逻辑
  • 隐藏目标对象的复杂性(如远程服务调用)。

📌 典型应用

  • Spring AOP(基于 JDK/CGLIB 代理);
  • MyBatis 延迟加载;
  • RMI 远程方法调用;
  • 图片懒加载(先显示占位图,再加载真实图片)。

3、示例程序:买房代理系统(优化版)

1)接口与目标类

/**
 * 买房接口(Subject)
 */
public interface BuyHouse {
    void buyHouse(String address);
}

/**
 * 真实买房人(Real Subject)
 */
public class BuyHouseImpl implements BuyHouse {
    @Override
    public void buyHouse(String address) {
        System.out.println("【真实操作】正在购买房产: " + address);
    }
}

2)静态代理(增强版)

/**
 * 静态代理(Static Proxy)
 * 在调用前后添加业务逻辑
 */
public class BuyHouseProxy implements BuyHouse {
    private final BuyHouse target;

    public BuyHouseProxy(BuyHouse target) {
        this.target = target;
    }

    @Override
    public void buyHouse(String address) {
        // 前置处理:身份验证
        System.out.println("【代理】验证客户购房资格...");
        
        // 调用真实对象
        target.buyHouse(address);
        
        // 后置处理:记录日志
        System.out.println("【代理】已记录本次购房行为到审计日志");
    }
}

3)JDK 动态代理(通用日志代理)

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

/**
 * 通用日志动态代理处理器
 */
public class LoggingInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【JDK代理】调用方法: " + method.getName() + ", 参数: " + Arrays.toString(args));
        Object result = method.invoke(target, args);
        System.out.println("【JDK代理】方法执行完毕");
        return result;
    }
}
public class JdkProxyTest {
    public static void main(String[] args) {
        BuyHouse real = new BuyHouseImpl();
        InvocationHandler handler = new LoggingInvocationHandler(real);

        BuyHouse proxy = (BuyHouse) Proxy.newProxyInstance(
            BuyHouse.class.getClassLoader(),
            new Class[]{BuyHouse.class},
            handler
        );

        proxy.buyHouse("北京市朝阳区");
    }
}

4)CGLIB 动态代理(无接口场景)

适用场景:目标类 没有实现接口,JDK 代理无法使用。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * CGLIB 代理:无需接口,通过继承实现
 */
public class CglibProxy implements Method特派员 {
    @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;
    }

    @SuppressWarnings("unchecked")
    public <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
}
// 假设有一个没有实现接口的类
public class HouseService {
    public void purchase(String location) {
        System.out.println("【无接口类】购买房产: " + location);
    }
}

// 测试
public class CglibTest {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        HouseService service = proxy.createProxy(HouseService.class);
        service.purchase("上海市浦东新区");
    }
}

4、FQA

1)代理模式分类

  • JDK 代理:基于接口,轻量,Java 原生支持;
  • CGLIB 代理:基于继承,可代理类,性能略高,需引入第三方库。
类型 特点 示例
静态代理 编译期确定,手动编写代理类 BuyHouseProxy
JDK 动态代理 运行时生成代理类,仅支持接口 Proxy.newProxyInstance()
CGLIB 动态代理 运行时生成子类,支持类(无需接口) Enhancer.create()
其他代理 远程代理、虚拟代理、保护代理等 RMI、懒加载、权限控制

2)JDK 代理 vs CGLIB 代理

  • 目标对象 实现了接口 → 使用 JDK 代理
  • 目标对象 未实现接口 → 使用 CGLIB 代理
对比项 JDK 动态代理 CGLIB 动态代理
实现方式 基于接口(Proxy + InvocationHandler 基于继承(生成子类)
是否需要接口 必须 不需要
性能 略低(反射调用) 略高(FastClass 机制)
限制 不能代理类(只能代理接口) 不能代理 final 类/方法
依赖 Java 原生 需引入 cglib
Spring 默认 有接口时用 JDK 无接口时用 CGLIB

3)模式优点

  • 职责分离:真实对象专注核心逻辑,代理处理辅助功能;
  • 增强灵活性:可在不修改目标类的情况下扩展行为;
  • 控制访问:实现懒加载、权限校验、远程调用等;
  • 符合开闭原则:新增代理无需改动原有代码。

4)注意事项

  • 静态代理:每个目标类需手写代理,维护成本高;
  • 动态代理:性能略低于直接调用(但可接受);
  • CGLIB:不能代理 final 类或方法;
  • 调试复杂度:动态生成的代理类增加排查难度。