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

相关推荐
Ulyanov34 分钟前
Python实现6-DOF刚体仿真器(下)——环境扰动与控制闭环
开发语言·python·算法·系统仿真·雷达电子对抗·导引头
分布式存储与RustFS38 分钟前
RustFS Beta.10 性能解读:PUT 全面反超 MinIO,Rust 重写对象存储成了?
开发语言·后端·rust
小小的木头人1 小时前
Python 批量解析 Excel 经纬度,调用高德地图 API 获取中文地址
开发语言·python·excel
怎么比啊都是大神1 小时前
关于==null、isEmpty()和StrUtil.isBlank()
java
唐青枫1 小时前
Java Neo4j 实战指南:从图模型、Cypher 到 Spring Boot 关系查询
java
观远数据1 小时前
从离线开发到实时同步:DataFlow如何支撑企业级数据治理闭环
java·windows·microsoft·excel
nzz_1712142 小时前
PHP程序员转型AI岗位指南:核心技能、北京就业市场与转型路径分析
开发语言·人工智能·php
Risehuxyc2 小时前
C# 将doc转换为docx
开发语言·c#·xhtml
今天AI了吗2 小时前
Hermes Agent 搭建全流程:从本机试跑到可持续运行的个人 AI Agent
java·人工智能·python·学习·embedding
春卷同学3 小时前
032-关系型数据库在记账应用中的建模与查询优化
java·jvm·数据库