jdk动态代理和Cglib动态代理的区别,为什么Cglib更适配SpringAOP

一.jdk动态代理的原理

java 复制代码
package com.atguigu.springaop;

import com.atguigu.springaop.service.UserService;
import com.atguigu.springaop.service.impl.UserServiceImpl;

import java.lang.reflect.Proxy;

/**
 * JDK动态代理
 * 				必须基于接口一种代理方式。
 * 					目标类 和 代理类  都是接口的实现类。
 *
 * 			由于JDK动态代理通过反射创建在内存中创建代理类。扩展代码不能写在代理类中。
 *
 * 	                扩展代码要写在  InvocationHandler接口的invoke实现方法中。java.lang.reflect.InvocationHandler
 */
public class JdkDynamicProxyDemo {
    public static void main(String[] args) {
        //目标对象
        UserService userService = new UserServiceImpl();

        //创建代理对象。利用反射机制创建代理类对象。
        UserService proxyObject =  (UserService)Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                (proxyObj, method, argsArray) -> {

                    System.out.println("方法开始执行了..."); //非业务代码    每个方法开始都要执行这行代码,重复代码开发。不利于维护。

                    //反射调用目标方法。
                    Boolean flag = (Boolean)method.invoke(userService,argsArray);  // Boolean flag = userService.login(argsArray[0],argsArray[1]);

                    System.out.println("方法结束执行了...");  //非业务代码   每个方法开始都要执行这行代码,重复代码开发。不利于维护。
                    return flag;
                });

        boolean falg = proxyObject.login("admin", "123456");
        System.out.println("falg = " + falg);
    }
    /**
     * 在 JDK 动态代理中,我们先创建一个包含真实业务逻辑的目标对象(如 UserServiceImpl),再通过 Proxy.newProxyInstance()
     * 动态生成一个实现了相同接口的代理对象。这个代理对象本身不包含业务逻辑,它的核心是通过 InvocationHandler 拦截方法调用,
     * 在调用前后插入增强逻辑(如日志、事务),并通过 method.invoke() 反射调用目标对象的真实方法。代理对象是多例的,每次调用 newProxyInstance
     * 都会生成一个新实例,其生命周期由代码作用域决定,而非随单次方法调用结束而销毁。
     * */
}

二.Cglib动态代理的原理

  • DK 代理的 Method.invoke() 是纯反射调用,每次都要动态解析方法签名、检查权限,有固定开销;
  • CGLIB 的 MethodProxy.invokeSuper() 基于 FastClass 生成索引化调用逻辑,绕过反射的动态解析,高频调用场景下性能比 JDK 代理高 20%-30%(Spring 5+ 对 JDK 代理做了优化,差距缩小,但 CGLIB 仍略优)。
java 复制代码
 public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

    private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

    private static FastClass helper(CreateInfo ci, Class type) {
        FastClass.Generator g = new FastClass.Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }
}

MethodProxy 是 CGLIB 为代理方法生成的 "高性能调用器",核心解决普通反射 Method.invoke() 性能低的问题。这段代码的核心流程是:创建 MethodProxy → 懒加载初始化 FastClass → 为方法分配索引 → 通过索引快速调用

2.1逐方法拆解源码

1. create(...) 方法:初始化 MethodProxy 的基础元信息

java

运行

复制代码
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    MethodProxy proxy = new MethodProxy();
    proxy.sig1 = new Signature(name1, desc); // 目标类方法的签名
    proxy.sig2 = new Signature(name2, desc); // 代理类方法的签名
    proxy.createInfo = new CreateInfo(c1, c2); // 存储目标类/代理类信息
    return proxy;
}

参数 / 变量解读

  • c1:目标类(如 UserService,真正包含业务逻辑的类);
  • c2:CGLIB 动态生成的代理类(如 UserService$$EnhancerByCGLIB$$xxxx,目标类的子类);
  • desc:方法的字节码描述符(如 login 方法的描述符是 (Ljava/lang/String;Ljava/lang/String;)Z,代表 "两个 String 参数,返回 boolean");
  • name1:目标类中方法的原始名称(如 login);
  • name2:代理类中对应方法的别名(CGLIB 生成的,如 CGLIB$login$0);
  • sig1/sig2Signature 是 CGLIB 封装的 "方法签名"(方法名 + 描述符),用于唯一标识一个方法;
  • createInfo:临时存储目标类 / 代理类的核心信息(类加载器、命名规则等),供后续生成 FastClass 使用。

核心作用 :为目标方法和代理方法做 "身份标记"------ 相当于给方法办了 "身份证",后续生成 FastClass 时,能通过这个 "身份证" 找到对应的方法。

2. init() 方法:懒加载创建 FastClass(性能优化的核心)

java

运行

复制代码
private void init() {
    if (this.fastClassInfo == null) { // 双重检查锁:保证线程安全 + 懒加载
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                CreateInfo ci = this.createInfo;
                FastClassInfo fci = new FastClassInfo();
                // 1. 为目标类(c1)生成 FastClass
                fci.f1 = helper(ci, ci.c1); 
                // 2. 为代理类(c2)生成 FastClass
                fci.f2 = helper(ci, ci.c2); 
                // 3. 为目标方法/代理方法分配"索引"(核心!)
                fci.i1 = fci.f1.getIndex(this.sig1); // 目标方法在 FastClass 中的索引
                fci.i2 = fci.f2.getIndex(this.sig2); // 代理方法在 FastClass 中的索引
                this.fastClassInfo = fci; // 缓存 FastClass 信息,避免重复创建
                this.createInfo = null; // 释放临时信息,节省内存
            }
        }
    }
}

关键逻辑解读

  • 懒加载init() 不会在 create() 时执行,而是第一次调用 methodProxy.invokeSuper() 时才触发 ------ 如果代理方法从未被调用,就不用生成 FastClass,节省内存和初始化时间;
  • 双重检查锁(DCL) :保证多线程下只会生成一次 FastClass,避免重复创建导致的性能损耗;
  • FastClass 索引化fci.f1.getIndex(this.sig1) 是核心 ------FastClass 会为目标类的所有方法生成一个 "索引表"(方法名 → 整数索引),比如 login 方法的索引是 1logout2。后续调用方法时,只需传索引 1,不用再通过方法名反射找方法,这是性能提升的关键;
  • fastClassInfo:缓存 FastClass 和方法索引,后续调用直接复用,无需重复生成。
3. helper(...) 方法:生成 FastClass 实例

java

运行

复制代码
private static FastClass helper(CreateInfo ci, Class type) {
    FastClass.Generator g = new FastClass.Generator();
    g.setType(type); // 指定为哪个类生成 FastClass(目标类/代理类)
    g.setClassLoader(ci.c2.getClassLoader()); // 复用代理类的类加载器
    g.setNamingPolicy(ci.namingPolicy); // 遵循 CGLIB 的命名规则(如 FastClass 类名格式:XXX$$FastClassByCGLIB$$xxxx)
    g.setStrategy(ci.strategy); // 字节码生成策略(控制 FastClass 的生成规则)
    g.setAttemptLoad(ci.attemptLoad); // 尝试从已加载的类中获取 FastClass,避免重复生成
    return g.create(); // 核心:动态生成 FastClass 的字节码并加载到 JVM
}

FastClass 的本质FastClass 是 CGLIB 为目标类 / 代理类生成的 "快速调用类",它的核心逻辑是:

java

运行

复制代码
// 简化版 FastClass 生成的代码
public class UserService$$FastClassByCGLIB$$xxxx {
    // 方法索引表:索引 → 方法调用
    public Object invoke(int index, Object obj, Object[] args) {
        UserService target = (UserService) obj;
        switch(index) {
            case 1: return target.login((String)args[0], (String)args[1]); // 直接调用,无反射
            case 2: return target.logout();
            // ... 其他方法
        }
    }
}

可以看到:FastClass 把 "通过方法名反射调用" 变成了 "通过整数索引直接调用",完全是普通的 Java 方法调用,没有任何反射开销。


结合调用流程,看懂性能优化的本质

当你调用 methodProxy.invokeSuper(proxyObj, args) 时,底层执行流程是:

复制代码
methodProxy.invokeSuper(...) 
    → 触发 init()(首次调用)
        → helper() 生成目标类/代理类的 FastClass
        → 为目标方法分配索引(如 login 方法索引=1)
    → 调用 FastClass.invoke(1, proxyObj, args)
        → 通过 switch(index) 直接调用目标方法(无反射)

2.2code:

java 复制代码
package com.atguigu.springaop;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

// ========== 1. 目标类(无需实现接口,仅做核心业务) ==========
class UserService {
    public boolean login(String username, String password) {
        System.out.println("UserService - login 目标方法执行了");
        // 核心业务:登录校验
        return "admin".equals(username) && "123456".equals(password);
    }
}

// ========== 2. CGLIB动态代理核心:通用增强逻辑 + 生成代理 ==========
public class CglibDynamicProxyDemo {
    public static void main(String[] args) {
        // 步骤1:创建目标对象
        UserService target = new UserService();

        // 步骤2:生成动态代理对象(运行时自动创建代理类,继承目标类)
        UserService proxy = (UserService) Enhancer.create(
                UserService.class,  // 目标类的Class对象(核心:继承目标类)
                (MethodInterceptor) (proxyObj, method, argsArr, methodProxy) -> {
                    // 增强逻辑1:方法执行前(如打印日志)
                    System.out.println("Before " + method.getName() + ": " + argsArr[0]);

                    // 调用目标类的核心方法
                    Object result = methodProxy.invoke(target, argsArr); //通过方法代理对象调用目标方法

                    // 增强逻辑2:方法执行后(如打印结果)
                    System.out.println("After " + method.getName() + ": " + result);

                    return result;
                }
        );

        // 步骤3:调用代理方法(自动触发增强+核心业务)
        boolean flag = proxy.login("admin", "123456");
        System.out.println("flag = " + flag);
    }
    /**
     * 内部Enhancer e = new Enhancer();
     * e.setSuperclass(type);
     * e.setCallback(callback);
     * e.create();
     *
     * CGLIB动态代理通过`Enhancer`核心工具类实现,无需目标类实现接口,而是以目标类(如UserService)作为父类动态生成子类作为代理类;
     * Enhancer.create`方法指定目标类Class对象和`MethodInterceptor`回调接口,代理类的所有方法调用都会被该回调拦截,回调方法接
     * 收代理实例、被拦截方法的反射对象、方法实际参数值和CGLIB优化的`methodProxy`对象;调用代理对象方法时,会先执行前置增强逻辑,再
     * 通过`methodProxy.invoke`(性能优于普通反射)调用目标对象的核心业务方法,执行完成后执行后置增强逻辑并返回结果,这一机制弥补了
     * JDK动态代理需实现接口的局限性,同时通过方法代理优化提升了调用性能。
     *
     * */
}

三.对比

对比维度 JDK 动态代理(JDK Proxy) CGLIB 动态代理(Code Generation Library)
底层原理 基于接口实现:动态生成一个实现目标接口 的代理类(如 $Proxy0),代理类的方法转发给 InvocationHandler 基于继承实现:动态生成目标类的子类 作为代理类,重写目标方法并转发给 MethodInterceptor
核心限制 必须要求目标类实现至少一个接口(否则无法生成代理) 目标类不能是 final(无法继承),目标方法不能是 final(无法重写)
核心类 / 接口 Proxy.newProxyInstance()InvocationHandler Enhancer.create()MethodInterceptorMethodProxy
方法调用方式 Method.invoke()(纯反射,性能中等) MethodProxy.invokeSuper()(FastClass 索引调用,性能更高)
侵入性 目标类必须实现接口(侵入式设计) 目标类无需实现任何接口(无侵入式设计)
实例化方式 代理类实现接口,可直接强转为接口类型 代理类继承目标类,可直接强转为目标类类型
Spring AOP 默认选择 目标类有接口 → 优先用 JDK 代理 目标类无接口 → 自动切换为 CGLIB 代理
相关推荐
自动化和Linux14 分钟前
C语言_scanf(),strlen(),size()的特性和各自的区别
c语言·开发语言
hx8622719 分钟前
Java MySQL 连接
java·mysql·adb
lpfasd12321 分钟前
Kubernetes (K8s) 底层早已不再直接使用 Docker 引擎了
java·docker·kubernetes
aq553560024 分钟前
SpringBoot有几种获取Request对象的方法
java·spring boot·后端
小郝 小郝37 分钟前
51 与32 单片机LED控制详解
c语言·开发语言·经验分享·学习·51单片机
星空露珠40 分钟前
迷你世界UGC3.0脚本Wiki全局函数
开发语言·数据库·算法·游戏·lua
金山几座1 小时前
C#学习记录-类(Class)
开发语言·学习·c#
AsDuang1 小时前
Python 3.12 MagicMethods - 55 - __irshift__
开发语言·python
Detachym1 小时前
InsightFlow 服务配置优化与部署实践
java·spring boot·tomcat·maven·状态模式·jar
y = xⁿ1 小时前
【LeetCodehot100】T23:合并k个升序链表
java·数据结构·链表