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 代理
相关推荐
楼田莉子2 小时前
C++高精度时间库——<chrono>
开发语言·c++·后端·学习·visual studio
石牌桥网管2 小时前
Go类型断言
开发语言·后端·golang
塔中妖2 小时前
Windows 安装 Maven 详细教程(含镜像与本地仓库配置)
java·windows·maven
zh_xuan2 小时前
kotlin 高阶函数用法
开发语言·kotlin
colicode2 小时前
安卓Android语音验证码接口API示例代码:Kotlin/Java版App验证开发
android·java·前端·前端框架·kotlin·语音识别
程序员敲代码吗3 小时前
解析Kotlin中元组的多返回值实现
android·开发语言·kotlin
Java后端的Ai之路3 小时前
【 Java】-网络协议核心知识问答(比较全)
java·开发语言·网络协议
小道仙979 小时前
jenkins对接、jenkins-rest
java·servlet·jenkins·jenkins-rest
莫寒清10 小时前
MinIO
java