CGLIB 是 Spring 实现 AOP 的核心底层技术之一,它基于 ASM 字节码框架,在运行时生成目标类的子类来实现代理。相比于 JDK 动态代理(基于接口),CGLIB 可以代理普通的 Java 类,灵活性更高。
springboot 【spring-core-7.0.2.jar】已经将cglib,asm相关的代码作为其core代码的一部分
从Springboot 2.x以后,默认统一使用 CGLIB, 针对有接口的则切换到JdkDynamicAopProxy,原因如下:
- 适用性更强(无需强制实现接口):CGLIB 通过生成子类来实现代理,不需要你的业务类必须实现某个接口。这符合现代开发中更倾向于直接面向类编程的趋势,减少了开发约束。
- 性能更优:JDK 动态代理底层依赖 Java 反射(
InvocationHandler),性能相对较低;而 CGLIB 底层使用 ASM 框架直接操作字节码,通过FastClass机制进行方法调用,跳过了反射的性能损耗,执行效率更高。
在 Spring AOP 中,使用注解(如@Transactional)来声明需求,Spring 底层则会选择使用JDK 动态代理或CGLIB来实现这个需求
CGLIB底层实现原理
CGLIB 并不是 Spring 自己写的,而是一个强大的第三方字节码生成库(底层依赖 ASM 框架)。
- 核心机制:继承。
CGLIB 在运行时动态地生成一个被代理类的子类(final类除外,因为不能被继承)。 - 拦截逻辑:
这个生成的子类会**重写(Override)**父类中所有的非final方法。在重写的方法中,它会插入拦截器逻辑(MethodInterceptor)。 - 执行流程:
当你调用代理对象的方法时,实际上调用的是子类重写后的方法。子类在执行时,会先通知拦截器(执行切面逻辑,如日志、事务),然后再通过反射调用父类(即目标对象)的原始方法。
简单总结:CGLIB 就是通过“继承你的类,重写你的方法”来实现功能增强的
与JDK动态代理的区别
| 对比维度 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现机制 | Java 反射 API (Proxy+InvocationHandler) | 字节码操作(ASM 框架,生成子类) |
| 对接口的依赖 | 必须实现接口 | 不需要实现接口(直接代理类) |
| 性能 | 相对较低(依赖反射调用) | 较高(通过 FastClass 调用,接近原生) |
| 限制 | 只能代理接口中定义的方法 | 不能代理final类或final方法 |
| Spring Boot 默认 | 非默认 | 默认 |
如何配置代理方式
- 通过配置文件设置
# 默认使用CGLIB,# 如果设置为False,则切换为JAVA动态代理,但如果目标类没有实现接口,Spring 会自动降级回 CGLIBspring:aop:proxy-target-class:true- 通过注解设置
@EnableAspectJAutoProxy(proxyTargetClass = true)
CGLIB何时生成代理类
CGLIB 生成代理类是在运行时(Runtime)完成的,而不是在编译阶段,CGLIB 是一种运行时代码生成技术,
当你的 Spring Boot 应用启动,或者程序执行到动态代理创建逻辑时(例如调用Enhancer.create()),CGLIB 才会通过 ASM 框架在内存中动态生成一个新的字节码文件(即代理类),类名类似UserServiceImpl$$EnhancerByCGLIB$$e073c71b
CGLIB 的核心组件是Enhancer。当你调用enhancer.create()时,底层发生了以下事情:
- 触发:程序运行到创建代理对象的代码。
- 生成字节码:CGLIB 使用 ASM 库在 JVM 内存中,根据你的目标类(父类),动态编写一个新的类(子类)的字节码。
- 加载:通过类加载器将这个新生成的字节码加载到 JVM 中。
- 实例化:创建该代理类的实例并返回。
Springboot 代理的自动选择策略
在 Spring Boot(实际上是 Spring Framework 的 AOP 模块)中,这个自动选择策略(即:判断是用 JDK 动态代理还是 CGLIB)的源码核心位于DefaultAopProxyFactory类中
publicclassDefaultAopProxyFactoryimplementsAopProxyFactory,Serializable{publicstaticfinalDefaultAopProxyFactoryINSTANCE=newDefaultAopProxyFactory();privatestaticfinallongserialVersionUID=7930414337282325166L;publicAopProxycreateAopProxy(AdvisedSupportconfig)throwsAopConfigException{if(!config.isOptimize()&&!config.isProxyTargetClass()&&config.hasUserSuppliedInterfaces()){returnnewJdkDynamicAopProxy(config);}else{Class<?>targetClass=config.getTargetClass();if(targetClass==null&&config.getProxiedInterfaces().length==0){thrownewAopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");}else{return(AopProxy)(targetClass!=null&&!targetClass.isInterface()&&!Proxy.isProxyClass(targetClass)&&!ClassUtils.isLambdaClass(targetClass)?newObjenesisCglibAopProxy(config):newJdkDynamicAopProxy(config));}}}}示例:
| 场景 | 目标类实现的接口 | hasUserSuppliedProxyInterfaces结果 | Spring 的决策 |
|---|---|---|---|
| 场景 A | 实现了UserService(你的业务接口) | true | 默认使用JDK 动态代理 |
| 场景 B | 什么接口都没实现 | false | 使用CGLIB代理 |
| 场景 C | 实现了ApplicationContextAware | false | 使用CGLIB代理 (因为这是 Spring 内部接口) |
| 场景 D | 实现了SpringProxy(极少情况) | false | 使用CGLIB代理 (因为这是 Spring 自己的标记) |
调用链路
在 Spring Boot 启动过程中,这个逻辑是在 Bean 初始化的后置处理器中被触发的:
- 入口:
AbstractAutoProxyCreator.postProcessAfterInitialization()。 - 包装判断:
wrapIfNecessary()方法被调用。 - 创建代理:如果发现当前 Bean 需要被 AOP 增强,则调用
createProxy()。 - 工厂决策:
ProxyFactory会调用上述的DefaultAopProxyFactory.createAopProxy()来获取具体的代理实例。ProxyFactory本身不直接实现创建逻辑,它继承自ProxyCreatorSupport,而ProxyCreatorSupport持有一个非常关键的组件:AopProxyFactory- 当你调用
proxyFactory.getProxy()时,它内部会调用父类ProxyCreatorSupport的createAopProxy()方法。 ProxyCreatorSupport.createAopProxy(): 它负责从配置中获取AopProxyFactory并调用其createAopProxy()方法- 默认情况下,
ProxyCreatorSupport中持有的AopProxyFactory实例就是DefaultAopProxyFactory
何时使用Objenesis
在 Spring 框架中,objenesis相关的包(主要位于org.springframework.objenesis)提供了一个非常核心且底层的功能:绕过构造函数来实例化对象。
关于选择Objenesis还是反射(Reflection)来实例化对象,主要发生在 AOP 代理创建和某些特殊 Bean 的实例化过程中。
Spring 的选择策略可以概括为:“Objenesis 优先,反射兜底”。
- 原因:
- 性能更高:Objenesis 通过直接操作字节码或 Unsafe 机制绕过构造函数,比反射调用构造函数更快。
- 兼容性更强:它不需要目标类有无参构造函数,甚至可以实例化
final类(在某些模式下)或构造函数为private的类。
详见ObjenesisCglibAopProxy类
protectedObjectcreateProxyClassAndInstance(Enhancerenhancer,Callback[]callbacks){Class<?>proxyClass=enhancer.createClass();ObjectproxyInstance=null;if(objenesis.isWorthTrying()){try{proxyInstance=objenesis.newInstance(proxyClass,enhancer.getUseCache());}catch(Throwableex){logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction",ex);}}if(proxyInstance==null){try{Constructor<?>ctor=this.constructorArgs!=null?proxyClass.getDeclaredConstructor(this.constructorArgTypes):proxyClass.getDeclaredConstructor();ReflectionUtils.makeAccessible(ctor);proxyInstance=this.constructorArgs!=null?ctor.newInstance(this.constructorArgs):ctor.newInstance();}catch(Throwableex){thrownewAopConfigException("Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well",ex);}}((Factory)proxyInstance).setCallbacks(callbacks);returnproxyInstance;}与反射的区别
| 特性 | Objenesis (首选) | 反射 (兜底) |
|---|---|---|
| 实现原理 | 利用 JVM 底层 API (如Unsafe/ReflectionFactory) 绕过构造函数。 | 基于java.lang.reflect.Constructor的标准反射机制。 |
| 性能 | 极高。直接分配内存,不执行构造函数逻辑。 | 较低。涉及 JNI 调用和安全检查。 |
| 构造函数调用 | 不调用。对象创建后,成员变量保持 JVM 默认值(null/0)。 | 调用。会执行构造函数中的代码逻辑。 |
| 适用场景 | 代理类(Proxy)、序列化框架、需要绕过复杂构造逻辑的场景。 | 普通的 Bean 实例化、需要执行构造函数初始化逻辑的场景。 |
| 风险 | 对象状态可能不完整(未初始化);在某些受限的 JVM 环境(如高版本 JDK 的安全管理器)下可能被禁止。 | 安全、标准,但受限于构造函数的可见性和参数要求。 |
对象何时初始化
对象实例属性的初始化被推迟到了 Spring 的**属性填充(Populate)和初始化(Initialize)**阶段
- 依赖注入(DI):Spring 使用反射机制(
BeanUtils/ReflectionUtils)扫描@Autowired、@Value等注解,并通过set方法或直接Field.set()将依赖的对象或值注入进去。 - 显式赋值:如果需要特定的初始值,通常需要在
@PostConstruct注解的方法中,或者InitializingBean的afterPropertiesSet()方法中手动赋值。
使用 CGLIB 时的注意事项
- 不能代理
final类:因为 CGLIB 是通过继承实现的,如果类被声明为final,无法被继承,代理就会失败。 - 不能代理
final方法:同理,如果方法被声明为final,子类无法重写该方法,因此无法织入切面逻辑。 - 构造函数调用:由于是子类代理,创建代理对象时可能会调用目标类的构造函数,如果构造函数中有复杂的逻辑或副作用,需要留意。
- 依赖引入:在 Spring Boot 中你通常不需要手动引入,因为
spring-boot-starter-aspectj、spring-core-中已经包含了 CGLIB 和 ASM 的依赖。
使用示例
importorg.junit.jupiter.api.Test;importorg.springframework.cglib.proxy.Enhancer;importorg.springframework.cglib.proxy.MethodInterceptor;importorg.springframework.cglib.proxy.MethodProxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassProxyTest{@interfaceAuthor{Stringname();intage()default1;}@TestpublicvoidjdkProxyTest(){UserServiceuserService=newUserServiceImpl();UserServiceproxy=(UserService)newJdkProxyHandler(userService).getProxy();proxy.saveUser("张三");}@TestpublicvoidcglibProxyTest(){UserServiceuserService=newUserServiceImpl();UserServiceproxy=(UserService)newCglibProxyInterceptor(userService).getProxy();proxy.saveUser("张三");}}interfaceUserService{voidsaveUser(Stringname);voiddeleteUser(Longid);}classUserServiceImplimplementsUserService{@OverridepublicvoidsaveUser(Stringname){System.out.println("业务逻辑:正在保存用户 -> "+name);}@OverridepublicvoiddeleteUser(Longid){System.out.println("业务逻辑:正在删除用户 ID -> "+id);}}classJdkProxyHandlerimplementsInvocationHandler{privateObjecttarget;publicJdkProxyHandler(Objecttarget){this.target=target;}/** * 获取代理对象 */publicObjectgetProxy(){returnProxy.newProxyInstance(target.getClass().getClassLoader(),// 类加载器target.getClass().getInterfaces(),// 目标对象实现的接口this);// 当前的 InvocationHandler}/** * 拦截方法调用 */@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("[JDK代理] 方法执行前:日志开始...");// 执行目标方法Objectresult=method.invoke(target,args);System.out.println("[JDK代理] 方法执行后:日志结束。");returnresult;}}classCglibProxyInterceptorimplementsMethodInterceptor{// 被代理的对象privateObjecttarget;publicCglibProxyInterceptor(Objecttarget){this.target=target;}/** * 获取代理对象 */publicObjectgetProxy(){// 1. 创建增强器Enhancerenhancer=newEnhancer();// 2. 设置父类(即目标类)enhancer.setSuperclass(target.getClass());// 3. 设置回调(即拦截器)enhancer.setCallback(this);// 4. 创建子类(代理对象)returnenhancer.create();}/** * 拦截方法调用 */@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("[CGLIB代理] 方法执行前:日志开始...");// 注意:这里通常使用 proxy.invokeSuper(obj, args) 而不是反射// proxy.invokeSuper 调用的是父类的方法,性能更好Objectresult=proxy.invokeSuper(obj,args);System.out.println("[CGLIB代理] 方法执行后:日志结束。");returnresult;}}