那些超经典的设计模式—代理模式

好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.

代理模式是java中很常见的设计模式,如下将好好讲解下java代理模式相关的知识。本文还是将从:它的前世今生;使用场景;注意事项;代码样例等四个方面,详细阐述下代理模式的细节内容。

1. 前世今生:代理模式的演进史

阶段年代关键词里程碑事件
原始形态1995 以前手写包装类C/C++ 时代,通过 函数指针 + 结构体 实现“壳”与“核”分离。
Java 静态代理1996-2003interface + 组合Java 1.1 引入反射,RMI 用静态代理做远程调用。
JDK 动态代理2004java.lang.reflect.ProxyJDK 1.3 正式加入,Spring 1.x 用其构建 AOP。
CGLIB/字节码时代2005-2015ASM、JavassistHibernate、Spring 3.x 引入 CGLIB,解决“无接口”代理痛点。
新时代2016-至今ByteBuddy、LambdaMetafactoryJava 14 Proxy.newProxyInstance 性能提升 10×;Spring 6 原生镜像继续演进。

一句话总结:从“人肉手写”到“编译期织入”,再到“运行时字节码”,代理模式始终围绕 “在不修改原对象的前提下增加行为” 这一核心目标进化。


2. 使用场景:什么时候该用代理

场景举例代理类型
延迟加载(Lazy Loading)MyBatis 的 Mapper 代理动态代理
权限控制Spring Security 的 @PreAuthorizeAOP 代理
缓存Guava Cache、Spring Cache动态代理 + 拦截器
远程调用(RPC)Dubbo、Feign动态代理
日志 / 监控Micrometer、SLF4J 桥接动态代理
事务管理Spring @TransactionalAOP 代理
保护代理只读视图(Collections.unmodifiableList)静态代理

3. 注意事项:避坑指南

  1. 接口 vs 类

    • JDK 动态代理只能代理 接口,否则抛 IllegalArgumentException

    • 需要代理类请用 CGLIB、ByteBuddy。

  2. 性能

    • 反射 + 代理调用 ≈ 直接调用的 10~20 倍开销(JDK8 后优化到 2~3 倍)。

    • 高频调用场景(如 Netty pipeline)建议用 MethodHandle 或编译期织入。

  3. 循环代理

    • Spring Bean 之间的循环依赖 + AOP 代理会导致 提前暴露半成品代理,需开启 allowRawInjectionDespiteWrapping=true

  4. final / private 方法

    • CGLIB 无法代理 finalprivate 方法;JDK 代理压根不会拦截这些方法。

  5. 内存泄漏

    • 每次 Proxy.newProxyInstance 都会生成新类,滥用会导致 Metaspace OOM

    • 推荐缓存 Proxy 实例或使用 WeakCache 策略。

  6. 调试困难

    • 动态代理类名形如 $Proxy0,IDE 断点时可加 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 导出 .class 文件。


4. 代码样例:从静态到动态,从 JDK 到 CGLIB

4.1 静态代理

// 1. 业务接口
public interface OrderService {
    void createOrder(String orderNo);
}

// 2. 真实实现
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderNo) {
        System.out.println("创建订单:" + orderNo);
    }
}

// 3. 静态代理
public class OrderServiceStaticProxy implements OrderService {
    private final OrderService target;

    public OrderServiceStaticProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder(String orderNo) {
        System.out.println("[日志] 开始创建订单");
        target.createOrder(orderNo);
        System.out.println("[日志] 订单创建完成");
    }
}

// 4. 客户端
public class StaticProxyDemo {
    public static void main(String[] args) {
        OrderService proxy = new OrderServiceStaticProxy(new OrderServiceImpl());
        proxy.createOrder("202507170001");
    }
}

输出:

[日志] 开始创建订单
创建订单:202507170001
[日志] 订单创建完成

4.2 JDK 动态代理

public class JdkProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("[JDK动态代理] 前置逻辑");
                    Object result = method.invoke(target, args);
                    System.out.println("[JDK动态代理] 后置逻辑");
                    return result;
                });
    }

    public static void main(String[] args) {
        OrderService proxy = createProxy(new OrderServiceImpl());
        proxy.createOrder("202507170002");
    }
}

4.3 CGLIB 动态代理

public class CglibProxyFactory {
    public static <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
            System.out.println("[CGLIB] 前置");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("[CGLIB] 后置");
            return result;
        });
        return clazz.cast(enhancer.create());
    }

    // 无需接口
    public static class PlainOrderService {
        public void pay(String orderNo) {
            System.out.println("支付订单:" + orderNo);
        }
    }

    public static void main(String[] args) {
        PlainOrderService proxy = createProxy(PlainOrderService.class);
        proxy.pay("202507170003");
    }
}

4.4 Spring AOP 实战(注解驱动)

@Component
public class UserService {
    @Cacheable("user")
    public User findUser(Long id) {
        System.out.println("查询数据库:" + id);
        return new User(id, "Alice");
    }
}

// 启动类
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Application.class);
        UserService userService = ctx.getBean(UserService.class);
        userService.findUser(1L); // 第一次:查库
        userService.findUser(1L); // 第二次:读缓存
    }
}

5. 结语

代理模式是 Java 生态的“隐形发动机”:

  • 在框架层,它让 事务、缓存、安全、RPC 透明化;

  • 在业务层,它让 延迟加载、监控、灰度发布 开箱即用。

掌握代理模式,不只是会写 Proxy.newProxyInstance,更是理解 “开闭原则” 如何在字节码层面落地。祝你编码愉快,代理常伴!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值