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

相关推荐
学长毕设4 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
C^h4 小时前
c++ string
开发语言·c++
raindayinrain5 小时前
c++高性能--原子操作,无锁编程,操作顺序
开发语言·c++·性能优化
学逆向的5 小时前
C++ 合并命名空间
开发语言·c++·网络安全
Zhao-Jichao5 小时前
VS2026 打包 C++ 程序生成 EXE,借助 Installer Project 扩展工具
开发语言·c++
zzu123zsw6 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
学逆向的6 小时前
C 局部静态变量
c语言·开发语言·网络安全
大都督会赢的6 小时前
C++第一节课
开发语言·c++
“AI国潮设计-小江”6 小时前
【AIGC实战】Python+Stable Diffusion打造“英歌舞财神”IP,解锁“马到成功”等爆款情绪营销文案设计
开发语言·人工智能·python·prompt·aigc
源码集结号6 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统