SpringAOP:Java 三大代理模式:从静态代理到 JDK & CGLIB 动态代理

SpringAOP:Java 三大代理模式:从静态代理到 JDK & CGLIB 动态代理


---JavaEE专栏---

🚀 前言

在 Java 后端开发中,代理模式是 Spring AOP 的底层基石。无论是事务管理(@Transactional)还是日志拦截,都离不开代理。很多开发者只停留在概念层面,今天我们通过"房东租房"的实战案例,彻底吃透静态代理、JDK 动态代理与 CGLIB 动态代理的差异。


1. 业务基石:定义接口与目标类

在代理模式中,接口是代理类与目标类共同遵守的"契约"。

java 复制代码
package cn.overthinker.aop.demo.proxy;

/**
 * 业务接口:房屋业务
 */
public interface HouseSubject {
    void rentHouse(); // 出租房子
    void saleHouse(); // 出售房子
}

/**
 * 目标对象:房东(RealSubject)
 */
public class RealHouseSubject implements HouseSubject {
    @Override
    public void rentHouse() {
        System.out.println("我是房东:正在签署出租协议...");
    }

    @Override
    public void saleHouse() {
        System.out.println("我是房东:正在签署卖房协议...");
    }
}

2. 第一阶段:静态代理 (Static Proxy)

静态代理通过手动创建一个代理类,持有目标对象的引用,并在调用前后增加逻辑。

java 复制代码
package cn.overthinker.aop.demo.proxy;

/**
 * 静态代理:房屋中介
 */
public class HouseProxy implements HouseSubject {
    private HouseSubject realHouseSubject;

    public HouseProxy(HouseSubject realHouseSubject) {
        this.realHouseSubject = realHouseSubject;
    }

    @Override
    public void rentHouse() {
        System.out.println("静态代理 [中介]:带客户看房,收取定金...");
        realHouseSubject.rentHouse();
        System.out.println("静态代理 [中介]:交易完成,打扫卫生。");
    }

    @Override
    public void saleHouse() {
        System.out.println("静态代理 [中介]:发布售房广告...");
        realHouseSubject.saleHouse();
        System.out.println("静态代理 [中介]:协助办理过户手续。");
    }
}

❌ 缺点: 每一个业务类都要手动写一个代理类,代码冗余。如果接口增加方法,代理类必须同步修改,维护性极差。


3. 第二阶段:JDK 动态代理 (JDK Dynamic Proxy)

JDK 动态代理不再需要手动写 HouseProxy 类,而是通过反射在内存中动态生成。前提:目标类必须实现接口。

java 复制代码
package cn.overthinker.aop.demo.proxy;

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

public class JDKInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理 [中介]:开始代理逻辑(日志记录/鉴权)...");
        // 反射执行目标方法
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理 [中介]:结束代理逻辑。");
        return result;
    }
}

4. 第三阶段:CGLIB 动态代理 (CGLIB Proxy)

CGLIB 弥补了 JDK 的不足:它不需要接口。它通过字节码技术生成目标类的子类

java 复制代码
package cn.overthinker.aop.demo.proxy;

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

public class CglibMethodInterceptor implements MethodInterceptor {
    private Object target;

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB动态代理 [中介]:预处理流程...");
        // 调用目标方法
        Object result = method.invoke(target, args);
        System.out.println("CGLIB动态代理 [中介]:后期收尾操作...");
        return result;
    }
}

5. 整合运行测试类 (Main)

java 复制代码
package cn.overthinker.aop.demo.proxy;

import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        // 1. 创建目标对象
        HouseSubject target = new RealHouseSubject();

        // --- 静态代理测试 ---
        System.out.println("==== 静态代理 ====");
        HouseProxy staticProxy = new HouseProxy(target);
        staticProxy.rentHouse();

        // --- JDK 动态代理测试 ---
        System.out.println("\n==== JDK 动态代理 ====");
        HouseSubject jdkProxy = (HouseSubject) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{HouseSubject.class},
                new JDKInvocationHandler(target)
        );
        jdkProxy.rentHouse();

        // --- CGLIB 动态代理测试 ---
        System.out.println("\n==== CGLIB 动态代理 ====");
        HouseSubject cglibProxy = (HouseSubject) Enhancer.create(
                target.getClass(),
                new CglibMethodInterceptor(target)
        );
        cglibProxy.saleHouse();
    }
}

6. 深度解析:Spring 如何选择代理?

根据 Spring 源码中 DefaultAopProxyFactory 的逻辑(如你 PDF 资料中提到的):

  1. 如果有接口:默认使用 JDK 代理。
  2. 如果没有接口:强制使用 CGLIB 代理。
  3. Spring Boot 2.x+ 变化 :默认设置 spring.aop.proxy-target-class=true,这意味着即使有接口,也倾向于使用 CGLIB

为什么 Spring Boot 2.x 倾向于 CGLIB?

因为 JDK 代理生成的类是 com.sun.proxy.$ProxyXX,如果你在业务代码中尝试注入实现类(@Autowired private RealHouseSubject service),JDK 代理会因为类型不匹配抛出异常。而 CGLIB 生成的是目标类的子类,完美兼容。


7. 严谨总结与对比

维度 静态代理 JDK 动态代理 CGLIB 动态代理
底层实现 硬编码 Java 反射机制 字节码操作 (ASM)
代理形式 静态实现接口 动态实现接口 动态生成子类 (继承)
依赖性 必须有接口 无需接口
局限性 维护性差 只能代理接口方法 不能代理 final 类/方法

🌟 结语

理解这三种模式,不仅能帮你从容应对面试中的"AOP 底层原理",更能让你在开发像 ChatRoom 这样的实时交互系统时,学会如何通过切面优雅地处理日志记录、限流防抖等通用业务。

相关推荐
largecode8 小时前
如何让电话显示店名?来电显示店铺名称,提升有效接通率
java·开发语言·spring·百度·学习方法·业界资讯·twitter
xuhaoyu_cpp_java8 小时前
SpringMVC学习(五)
java·开发语言·经验分享·笔记·学习·spring
Aurorar0rua8 小时前
CS50 x 2024 Notes C -11
c语言·开发语言·学习方法
Dlrb12118 小时前
C语言-指针
c语言·开发语言
计算机安禾8 小时前
【c++面向对象编程】第22篇:输入输出运算符重载:<< 与 >> 的友元实现
java·前端·c++
zhangzhi19798155928 小时前
Agent Skills
开发语言·python
旷世奇才李先生8 小时前
Java虚拟线程原理与实践
java
heimeiyingwang8 小时前
【架构实战】RPC框架Dubbo3.0:高性能Java通信之道
java·rpc·架构
i220818 Faiz Ul8 小时前
宠物猫之猫咖管理系统|基于java + vue宠物猫之猫咖管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·宠物猫之猫咖管理系统
Nyarlathotep01138 小时前
定时线程池:ScheduledThreadPoolExecutor
java·后端