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 这样的实时交互系统时,学会如何通过切面优雅地处理日志记录、限流防抖等通用业务。

相关推荐
小小测试开发2 小时前
安装 Python 3.10+
开发语言·人工智能·python
AAA大运重卡何师傅(专跑国道)4 小时前
【无标题】
开发语言·c#
XBodhi.4 小时前
Visual Studio C++ 语法错误: 缺少“;”(在“return”的前面)
开发语言·c++·visual studio
LSssT.5 小时前
【01】Python 机器学习
开发语言·python
心之伊始5 小时前
Java 后端接入大模型:从 Token、并发到推理成本的完整估算方法
java·spring boot·性能优化·大模型·llm
l1t5 小时前
DeepSeek总结的使用实体-组件-系统和基于存在性处理进行Python编程39-40
开发语言·python
BlackTurn6 小时前
技术经理投标
java
曾阿伦6 小时前
Python 搭建简易HTTP服务
开发语言·python·http
YG亲测源码屋6 小时前
java配置环境变量、jdk环境变量配置、java环境变量设置方法
java·开发语言
MIUMIUKK6 小时前
从语法层面,看懂 Python 的特殊处
java·开发语言·python