新手也能学得会之 spring-core 内嵌的 CGLIB
本文探讨了Spring框架中CGLIB的实现与应用。Spring将CGLIB核心代码打包在spring-core中,主要用于AOP代理和@Configuration类增强,避免了外部依赖冲突。文章详细分析了CGLIB在Spring中的使用场景
一,spring-core 中的 CGLIB
在spring框架中代理模式基本都会使用到,因为它是spring Bean对象增强的来源。使用 Spring 通过 @Autowired 将Bean对象注入,而注入的实例都是通过代理模式提供一个Bean代理对象。
在spring-core组件里面,org.springframework.cglib 这个包里面就是实现CGLIB的代码。有同学就会有疑问,CGLIB不是有单独的依赖包吗?直接引入依赖该部署包不就可以了。

那这个 org.springframework.cglib 和这个有什么不同。Spring 是自己实现一个CGLIB吗?
首先 Spring 并没有自己实现一个 CGLIB 依赖包,而是把 cglib 的必要代码打包在 spring-core 里(放在 org.springframework.cglib 包下),主要是为了:
避免直接依赖外部 cglib 库(减少依赖冲突)
做一些轻量定制,适配 Spring 的运行时需求。
它的核心用途是 运行时动态生成类的子类(subclass),典型场景:
- AOP 代理:当目标类不是接口,或者不能用 JDK 动态代理时,Spring 用 cglib 生成子类来做代理。
- @Configuration 配置类增强:Spring 会用 cglib 生成配置类的子类,拦截 @Bean 方法,确保单例 bean 只创建一次。
- 其他内部工具:如反射调用优化、类拷贝、方法拦截等。
✅ 所以,org.springframework.cglib 更像是运行时生成类和代理的底层工具,并不直接负责创建你的 Bean 对象,而是提供生成代理类的能力。
二,CGLIB包的Bean是指定什么?
org.springframework.cglib.beans 这个包确实名字里有 "Bean",但它不是 Spring IoC 容器里 spring-beans 的“Bean”概念,而是 cglib 提供的一些面向 Java Bean 的字节码工具类,主要目的是方便对普通 JavaBean 的动态生成、属性访问和批量操作。
该 org.springframework.cglib.beans的类主要用于运行时创建、拷贝和映射 Bean,而不是 Spring IoC 中的 Bean 生命周期管理。
不是专门给 Spring IoC Bean 用的,而是 CGLIB 提供的一组 Java Bean 操作工具类,Spring 把它们“打包”到了 spring-core 中以便内部和用户都能使用。这些类是 对任意 Java Bean(符合 getter/setter 规范的类)进行操作的动态字节码生成工具。
2.1 讲解 beans 包具体类的作用

我给你逐个解释:
|
类名 |
作用 |
场景示例 |
|
BeanCopier |
高效地将一个 Bean 的属性值拷贝到另一个 Bean(相同属性名和类型才会复制),性能比 Apache BeanUtils 快 |
DTO ↔ Entity 互转 |
|
BeanGenerator |
在运行时生成一个全新的 Java Bean 类,可以动态添加属性 |
从 JSON Schema 生成动态对象 |
|
BeanMap |
将 Bean 转换成 或反向操作(Map 转 Bean),内部用字节码生成访问器 |
动态解析或修改 Bean 属性 |
|
BeanMapEmitter |
|
BeanMap 内部用 |
|
BulkBean |
批量访问和设置 Bean 的多个属性,避免循环反射调用 |
高性能对象属性批量读写 |
|
FixedKeySet (有时一起出现) |
|
BeanMap 内部结构 |
|
KeyFactory |
动态生成 key 对象的类工厂,用于缓存字节码生成结果 |
2.2 使用Spring-core 组件的 CGLIB包 生成代理
package com.toast.learn.spring.core.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author toast
* @time 2025/8/13
* @remark
*/
// 真实主题类(目标类)
class ToastService {
public void doSomething() {
System.out.println("执行业务逻辑...");
}
}
public class SpringCoreCglibExample {
public static void main(String[] args) {
// 创建 Enhancer(增强器)
Enhancer enhancer = new Enhancer();
// 设置父类(cglib 是通过生成子类实现代理的)
enhancer.setSuperclass(ToastService.class);
// 设置回调(拦截器)
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法执行前: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用原始方法
System.out.println("方法执行后: " + method.getName());
return result;
}
});
// 生成代理对象
ToastService proxy = (ToastService) enhancer.create();
// 调用方法
proxy.doSomething();
}
}
输出结果:
PS D:\work-space\spring_GROUP\spring-framework-6.2.7> ./gradlew run
> Task :spring-learn:run
方法执行前: doSomething
执行业务逻辑...
方法执行后: doSomething
首先一点就是Spring-core里面的 CGLIB 兼容JDK9+ 以后的模块化处理。
这是因为 JDK 9+ 模块系统(JPMS)引入的强封装性导致的。CGLIB 使用了反射来访问 ClassLoader.defineClass() 方法,但在 JDK 9 及以上版本中,这个方法被模块系统保护了,默认不允许外部库(如 CGLIB)通过反射访问。
如果是在平常JDK 9+以上,仅仅只是通过 cglib 依赖包组件来进行代理的话,就必须要进行显式开放相关模块权限。如果没有开放的话就会报错:
Caused by: net.sf.cglib.core.CodeGenerationException:
java.lang.reflect.InaccessibleObjectException-->
Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible:
module java.base does not "opens java.lang" to unnamed module @3b95a09c
【解决方案】
在项目启动的时候,你需要在运行 Java 程序时,显式开放相关模块权限,允许 CGLIB 使用反射。追加JVM参数。IDEA中可在【add VM Options】 追加该参数即可。
--add-opens java.base/java.lang=ALL-UNNAMED
JDK8 没有这个影响,因为JDK8 并没有模块化。
2.3 自定义 ProxyFactory 代理工厂
在学习spring框架的时候,在接触AOP时,有一句话是这样子说,目标类有接口则走JDK动态代理生成代理类,没有目标类则走CGLIB 。这个是在spring-aop里面。现在小编这边只有一个spring-core,所以自定义一个ProxyFacotry来实现这个功能。代码如下:
【用户测试类】
// ✅ 示例1:一个实现了多个接口的类
public interface IUserService {
void sayHello(String name);
}
public static class UserService implements IUserService {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name);
}
}
【产品测试类】
// ✅ 示例2:一个完全没有实现任何接口的普通类
public static class Product {
private String title;
private Integer price;
public Product(String title, Integer price) {
this.title = title;
this.price = price;
}
public void setTitle(String title) {
this.title = title;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getTitle() {
return title;
}
public Integer getPrice() {
return price;
}
@Override
public String toString() {
return "Product{" +
"title='" + title + '\'' +
", price=" + price +
'}';
}
}
【代理工厂类】一个专门产生代理对象的工厂
package com.toast.learn.spring.core.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.*;
import java.util.Arrays;
/**
* @author toast
* @time 2025/8/13
* @remark * 通用代理工厂:根据 Class 创建实例,并自动添加代理(JDK 或 CGLIB) 支持面向接口编程
*/
public class ProxyFactory {
/**
* 创建代理对象
* @param interfaceType 接口类型(用于返回值类型)
* @param implClass 实现类(可选,若无参则调用无参构造)
* @param args 构造参数(可选)
* @param <T> 接口泛型
* @return 代理对象
*/
public static <T> T create(Class<T> interfaceType, Class<? extends T> implClass, Object... args) {
try {
// 1. 创建目标对象(构造方法匹配更灵活)
T target = instantiateWithArgs(implClass, args);
// 2. 判断是否使用 JDK 动态代理
if (interfaceType.isInterface()) {
return createJDKProxy(interfaceType, target);
} else {
return createCGLIBProxy(implClass, args);
}
} catch (Exception e) {
throw new RuntimeException("创建代理对象失败", e);
}
}
private static <T> T instantiateWithArgs(Class<? extends T> implClass, Object... args)
throws Exception {
if (args == null || args.length == 0) {
return implClass.getDeclaredConstructor().newInstance();
}
// 构造器匹配:使用 isAssignableFrom
Constructor<?> ctor = Arrays.stream(implClass.getConstructors())
.filter(c -> {
Class<?>[] paramTypes = c.getParameterTypes();
if (paramTypes.length != args.length) return false;
for (int i = 0; i < paramTypes.length; i++) {
Class<?> wrapParamType = wrapPrimitive(paramTypes[i]);
Class<?> wrapArgType = wrapPrimitive(args[i].getClass());
if (!wrapParamType.isAssignableFrom(wrapArgType)) {
return false;
}
}
return true;
})
.findFirst()
.orElseThrow(() ->
new RuntimeException("没有匹配的构造方法: " + Arrays.toString(args))
);
@SuppressWarnings("unchecked")
T target = (T) ctor.newInstance(args);
return target;
}
// 基本类型 → 包装类型
private static Class<?> wrapPrimitive(Class<?> type) {
if (!type.isPrimitive()) return type;
if (type == int.class) return Integer.class;
if (type == long.class) return Long.class;
if (type == double.class) return Double.class;
if (type == float.class) return Float.class;
if (type == boolean.class) return Boolean.class;
if (type == char.class) return Character.class;
if (type == byte.class) return Byte.class;
if (type == short.class) return Short.class;
return type; // void.class 等
}
/**
* 重载:如果只传一个类(可能是具体类),用 CGLIB 代理
*/
public static <T> T create(Class<T> clazz) {
try {
return createCGLIBProxy(clazz);
} catch (Exception e) {
throw new RuntimeException("创建 CGLIB 代理失败", e);
}
}
// ==================== 私有方法:JDK 代理 ====================
private static <T> T createJDKProxy(Class<T> interfaceType, T target) {
InvocationHandler handler = new ProxyInvocationHandler<>(target);
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class<?>[]{interfaceType}, // ✅ 关键:明确使用 interfaceType,而不是 target.getClass().getInterfaces()
handler
);
return interfaceType.cast(proxy); // ✅ 安全类型转换,避免 unchecked 警告
}
// ==================== 私有方法:CGLIB 代理 ====================
@SuppressWarnings("unchecked")
private static <T> T createCGLIBProxy(Class<T> clazz, Object... args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new CGLIBMethodInterceptor());
if (args != null && args.length > 0) {
Class<?>[] argTypes = Arrays.stream(args)
.map(Object::getClass)
.toArray(Class[]::new);
return (T) enhancer.create(argTypes, args);
} else {
return (T) enhancer.create();
}
}
// ==================== 拦截器实现 ====================
/**
* JDK 代理的拦截器
*/
private static class ProxyInvocationHandler<T> implements InvocationHandler {
private final T target;
public ProxyInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
// 记录开始时间,用于性能监控
long startTime = System.currentTimeMillis();
// 方法调用前
logBefore(className, methodName, args);
try {
// 执行目标方法
Object result = method.invoke(target, args);
// 方法成功返回后
long cost = System.currentTimeMillis() - startTime;
logAfterReturning(className, methodName, cost, result);
return result; // 正常返回
} catch (InvocationTargetException ex) {
// 注意:method.invoke 抛出的是 InvocationTargetException
// 它的 cause 才是原始异常
long cost = System.currentTimeMillis() - startTime;
Throwable targetException = ex.getTargetException();
logAfterThrowing(className, methodName, cost, targetException);
// 向上抛出原始异常
throw targetException;
} catch (Exception ex) {
// 防御性捕获其他反射异常(一般不会走到这里)
long cost = System.currentTimeMillis() - startTime;
logAfterThrowing(className, methodName, cost, ex);
throw ex;
}
}
private void logBefore(String className, String methodName, Object[] args) {
StringBuilder argStr = new StringBuilder();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
argStr.append(i > 0 ? ", " : "")
.append(args[i] == null ? "null" : args[i].toString());
}
}
System.out.printf("【logBefore-JDK代理】%s.%s(%s) - 开始执行%n", className, methodName, argStr);
}
private void logAfterReturning(String className, String methodName, long cost, Object result) {
String resultStr = result == null ? "null" : result.toString();
System.out.printf("【logAfterReturning-JDK代理】%s.%s() - 执行成功,耗时: %dms,返回值: %s%n",
className, methodName, cost, resultStr);
}
private void logAfterThrowing(String className, String methodName, long cost, Throwable ex) {
System.out.printf("【logAfterThrowing-JDK代理】%s.%s() - 执行异常,耗时: %dms,异常: %s: %s%n",
className, methodName, cost, ex.getClass().getSimpleName(), ex.getMessage());
}
}
/**
* CGLIB 代理的增强拦截器:支持前置、后置、异常、性能监控
*/
private static class CGLIBMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String className = method.getDeclaringClass().getSimpleName();
String methodName = method.getName();
long startTime = System.currentTimeMillis();
// 【前置】方法调用前
logBefore(className, methodName, args);
try {
// 执行目标方法
Object result = proxy.invokeSuper(obj, args);
// 【后置】方法成功返回
long cost = System.currentTimeMillis() - startTime;
logAfterReturning(className, methodName, cost, result);
return result;
} catch (Throwable t) {
// 注意:CGLIB 中直接抛出的是原始异常(不是 InvocationTargetException)
long cost = System.currentTimeMillis() - startTime;
logAfterThrowing(className, methodName, cost, t);
// 向上抛出异常
throw t;
}
}
// ==================== 日志方法 ====================
private void logBefore(String className, String methodName, Object[] args) {
StringBuilder argStr = new StringBuilder();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
argStr.append(i > 0 ? ", " : "")
.append(args[i] == null ? "null" : args[i].toString());
}
}
System.out.printf("【logBefore-CGLIB代理】%s.%s(%s) - 开始执行%n", className, methodName, argStr);
}
private void logAfterReturning(String className, String methodName, long cost, Object result) {
String resultStr = result == null ? "null" : result.toString();
System.out.printf("【logAfterReturning-CGLIB代理】%s.%s() - 执行成功,耗时: %dms,返回值: %s%n",
className, methodName, cost, resultStr);
}
private void logAfterThrowing(String className, String methodName, long cost, Throwable ex) {
System.out.printf("【logAfterThrowing-CGLIB代理】%s.%s() - 执行异常,耗时: %dms,异常: %s: %s%n",
className, methodName, cost, ex.getClass().getSimpleName(), ex.getMessage());
}
}
}
【main方法测试】
// 主方法测试
public static void main(String[] args) {
Object[] prodArgs = new Object[]{"《你不会看的书》", Integer.valueOf(100)};
Product prodProxy = ProxyFactory.create(Product.class, Product.class, prodArgs);
IUserService userProxy = ProxyFactory.create(IUserService.class, UserService.class, new Object[0]);
userProxy.sayHello("一号用户");
System.out.println(prodProxy.toString());
}
输出结果:
> Task :spring-learn:run
【logBefore-JDK代理】IUserService.sayHello(一号用户) - 开始执行
Hello 一号用户
【logAfterReturning-JDK代理】IUserService.sayHello() - 执行成功,耗时: 5ms,返回值: null
=================华丽的分割线===================
【logBefore-CGLIB代理】Product.toString() - 开始执行
【logAfterReturning-CGLIB代理】Product.toString() - 执行成功,耗时: 13ms,返回值: Product{title='《你不会看的书》', price=100}
Product{title='《你不会看的书》', price=100}
有同学会好奇,为什么 【logAfterReturning-CGLIB代理】 的代理打印会在toString() 前面。首先执行顺序如下:
1. System.out.println(prodProxy.toString());
↓
2. CGLIB 拦截 toString() 调用
↓
3. logBefore(...) 输出:【logBefore-CGLIB代理】... 开始执行
↓
4. 执行真正的 toString() → 返回字符串
↓
5. logAfterReturning(...) 输出:【logAfterReturning-CGLIB代理】... 执行成功
↓
6. intercept 返回字符串给 System.out.println
↓
7. System.out.println 输出:Product{title='...', price=100}
✅ 为什么看起来“日志先输出”?
因为:
logAfterReturning 是 System.out.printf(...) 直接输出的
prodProxy.toString() 的返回值也被 System.out.println(...) 输出
两者都是输出到 标准控制台(stdout),所以看起来像是:
【logAfterReturning...】执行成功,返回值: Product{...}
Product{...}
👉 这不是顺序错了,而是:
✅ logAfterReturning 打印的是“方法执行结果” ✅ System.out.println 打印的是“这个结果的内容”
它们输出的是 同一个字符串,只是上下文不同。
三 CGLIB 慢慢的退出历史舞台
现在CGLIB 已经不在维护了,最后一版是在2019年12月发布的。

并且近年来 ByteBuddy 正在逐步取代 CGLIB 成为 Java 中主流的字节码生成与操作库。这种趋势背后有技术演进、性能优化、易用性提升等多方面原因
3.1、CGLIB 的历史与局限
CGLIB(Code Generation Library)是早期非常流行的字节码生成库,广泛用于 Spring AOP、Hibernate 等框架中,通过继承方式动态生成代理类。
✅ CGLIB 的优点:
- 简单易用,API 相对直观。
- 不依赖接口,可以为类创建代理(基于子类)。
- 与 Spring 集成良好。
❌ CGLIB 的局限性:
- 基于 ASM 的旧版本:CGLIB 使用的是较老版本的 ASM(字节码操作框架),缺乏对新 Java 特性的支持(如 Lambda、模块系统、新字节码指令等)。
- 仅支持子类代理:必须继承目标类,不能对接口或 final 类进行代理(final 类无法继承)。
- 调试困难:生成的类名称混乱,堆栈信息难以追踪。
- 性能和内存占用问题:频繁生成类可能导致 Metaspace 内存溢出(尤其是 Java 8+)。
- 维护停滞:CGLIB 项目更新缓慢,社区活跃度低。
3.1、ByteBuddy 的崛起
ByteBuddy 是由 Rafael Winterhalter 开发的现代字节码操作库,设计目标是提供更安全、更灵活、更强大的 API 来生成和修改 Java 字节码。
✅ ByteBuddy 的核心优势:
|
🚀现代 Java 支持 |
完全支持 Java 8 到 Java 21+ 的新特性,包括模块系统、record、sealed class、invokedynamic 等。 |
|
🧱无需继承,更灵活 |
可以使用子类、接口实现、甚至直接修改已有类(redefine/retransform),支持更多场景。 |
|
🛠️类型安全的 DSL |
提供流式、类型安全的 API,减少出错,代码可读性强。例如: |
|
🔍强大的调试支持 |
可以命名生成的类、保存生成的字节码文件,便于调试和分析。 |
|
⚡性能优异 |
基于最新版 ASM,生成效率高,且支持缓存、延迟加载等优化。 |
|
🔄支持运行时类修改 |
支持 |
|
🧩与现代框架深度集成 |
被广泛应用在Mockito 3+、Spring Boot(部分场景)、Docker 测试(Testcontainers)、Micrometer、Java Agent 开发等中。 |
3.3、ByteBuddy 取代 CGLIB 的典型场景
|
Mock 框架升级 |
Mockito 从 2.x 升级到 3.x 后,完全用 ByteBuddy 替代了 CGLIB 和 Objenesis,提升了性能和兼容性。 |
|
Java Agent 开发 |
ByteBuddy 提供了极简的 API 来编写 Java Agent,比如监控、日志注入、性能分析等。 |
|
AOP / 动态代理增强 |
更灵活地实现方法拦截、注解处理、性能埋点等。 |
|
框架底层扩展 |
如 Spring Native、Quarkus 等新兴框架更倾向于使用现代字节码工具。 |
3.4、示例对比:CGLIB vs ByteBuddy
CGLIB 示例(创建代理)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Before method: " + method.getName());
return proxy.invokeSuper(obj, args);
});
MyService proxy = (MyService) enhancer.create();
ByteBuddy 示例(更清晰、类型安全)
MyService proxy = new ByteBuddy()
.subclass(MyService.class)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(Interceptor.class))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance();
// 拦截器类
public class Interceptor {
@RuntimeType
public static Object intercept(@SuperCall Callable<?> callable) throws Exception {
System.out.println("Before method");
try {
return callable.call();
} finally {
System.out.println("After method");
}
}
}
👉 可以看到,ByteBuddy 的 API 更加现代、类型安全、可组合,且易于扩展。
3.5、为什么开发者“喜爱”ByteBuddy?
- API 设计优雅:流式调用,链式编程,接近自然语言。
- 文档完善:官方文档详细,示例丰富(官网:https://bytebuddy.net)。
- 社区活跃:持续更新,支持新 Java 版本快。
- 功能强大:不仅能生成类,还能 retransform、redefine、支持 Java Agent。
- 生产级稳定性:已被多个主流项目验证,可靠性高。
3.6、总结:ByteBuddy 为何能取代 CGLIB?
|
维护状态 |
✅ 活跃维护(2024年仍在更新) |
⚠️ 基本停止维护(最后一次发布是 2019年12月) |
|
底层技术 |
基于 ASM,直接操作字节码 |
基于 ASM,但封装较老 |
|
API 设计 |
现代化、流式 API(Fluent API),易读易用 |
老旧 API,配置繁琐 |
|
JDK 兼容性 |
✅ 完美支持 JDK 9+ 模块系统 |
❌ 需要 |
|
代理方式 |
可生成子类、重写方法、甚至创建全新类 |
仅支持继承目标类生成子类 |
|
功能丰富性 |
支持注解、条件匹配、类型校验、Agent 注入等 |
功能较单一,仅基本代理 |
|
性能 |
更优(生成代码更高效) |
良好,但略逊于 ByteBuddy |
|
学习成本 |
中等偏高(功能强大) |
较低(简单场景容易上手) |
|
Spring 使用 |
Spring 6+ 内部部分模块开始倾向使用 |
Spring 5.x 主要使用 CGLIB |
✅ 结论:
ByteBuddy 并不是简单地“替代”CGLIB,而是以更现代、更安全、更强大的方式重新定义了 Java 字节码操作。随着 Java 生态向模块化、云原生、高性能方向发展,ByteBuddy 成为字节码操作的事实标准已是大势所趋。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)