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

相关推荐
呱牛do it几秒前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 5)
java·vue
练习时长一年1 分钟前
Spring配置类的演化
java·spring boot·spring
喜欢流萤吖~17 分钟前
服务间的依赖管理:微服务的协作之道
java·微服务
invicinble22 分钟前
Spring如何把bean注册到容器里
java·后端·spring
S1998_1997111609•X36 分钟前
MacOS/ˉsh(so.))os.apkair/AI
开发语言·网络·人工智能
SimpleLearingAI37 分钟前
C++虚函数详解
开发语言·c++
代码不加糖1 小时前
0基础搭建前后端分离项目:实现菜单与界面左右布局
java·前端·javascript·mysql·elementui·mybatis
希望永不加班1 小时前
SpringBoot 敏感数据脱敏(序列化层)
java·spring boot·后端·spring
Dxy12393102161 小时前
Python使用XPath定位元素:动态计算与函数调用
开发语言·python
希望永不加班1 小时前
SpringBoot 数据库索引优化:慢查询分析
java·数据库·spring boot·后端·spring