一,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<String,Object>

或反向操作(Map 转 Bean),内部用字节码生成访问器

动态解析或修改 Bean 属性

BeanMapEmitter

BeanMap的底层实现类,用 ASM 生成字节码,普通开发者基本不用直接调用

BeanMap 内部用

BulkBean

批量访问和设置 Bean 的多个属性,避免循环反射调用

高性能对象属性批量读写

FixedKeySet

(有时一起出现)

BeanMap内部使用的一个不可变 Key 集合

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}

✅ 为什么看起来“日志先输出”?

因为:

logAfterReturningSystem.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 的局限性:

  1. 基于 ASM 的旧版本:CGLIB 使用的是较老版本的 ASM(字节码操作框架),缺乏对新 Java 特性的支持(如 Lambda、模块系统、新字节码指令等)。
  2. 仅支持子类代理:必须继承目标类,不能对接口或 final 类进行代理(final 类无法继承)。
  3. 调试困难:生成的类名称混乱,堆栈信息难以追踪。
  4. 性能和内存占用问题:频繁生成类可能导致 Metaspace 内存溢出(尤其是 Java 8+)。
  5. 维护停滞:CGLIB 项目更新缓慢,社区活跃度低。

3.1、ByteBuddy 的崛起

ByteBuddy 是由 Rafael Winterhalter 开发的现代字节码操作库,设计目标是提供更安全、更灵活、更强大的 API 来生成和修改 Java 字节码。

✅ ByteBuddy 的核心优势:

🚀现代 Java 支持

完全支持 Java 8 到 Java 21+ 的新特性,包括模块系统、record、sealed class、invokedynamic 等。

🧱无需继承,更灵活

可以使用子类、接口实现、甚至直接修改已有类(redefine/retransform),支持更多场景。

🛠️类型安全的 DSL

提供流式、类型安全的 API,减少出错,代码可读性强。例如:new ByteBuddy().subclass(Object.class).defineMethod(...)

🔍强大的调试支持

可以命名生成的类、保存生成的字节码文件,便于调试和分析。

性能优异

基于最新版 ASM,生成效率高,且支持缓存、延迟加载等优化。

🔄支持运行时类修改

支持InstrumentationAPI,可在 JVM 运行时动态修改类(retransform),适用于 APM、热更新等场景。

🧩与现代框架深度集成

被广泛应用在Mockito 3+Spring Boot(部分场景)Docker 测试(Testcontainers)MicrometerJava 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?

  1. API 设计优雅:流式调用,链式编程,接近自然语言。
  2. 文档完善:官方文档详细,示例丰富(官网:https://bytebuddy.net)。
  3. 社区活跃:持续更新,支持新 Java 版本快。
  4. 功能强大:不仅能生成类,还能 retransform、redefine、支持 Java Agent。
  5. 生产级稳定性:已被多个主流项目验证,可靠性高。

3.6、总结:ByteBuddy 为何能取代 CGLIB?

维护状态

✅ 活跃维护(2024年仍在更新)

⚠️ 基本停止维护(最后一次发布是 2019年12月)

底层技术

基于 ASM,直接操作字节码

基于 ASM,但封装较老

API 设计

现代化、流式 API(Fluent API),易读易用

老旧 API,配置繁琐

JDK 兼容性

✅ 完美支持 JDK 9+ 模块系统

❌ 需要--add-opens才能在 JDK 9+ 使用

代理方式

可生成子类、重写方法、甚至创建全新类

仅支持继承目标类生成子类

功能丰富性

支持注解、条件匹配、类型校验、Agent 注入等

功能较单一,仅基本代理

性能

更优(生成代码更高效)

良好,但略逊于 ByteBuddy

学习成本

中等偏高(功能强大)

较低(简单场景容易上手)

Spring 使用

Spring 6+ 内部部分模块开始倾向使用

Spring 5.x 主要使用 CGLIB

结论
ByteBuddy 并不是简单地“替代”CGLIB,而是以更现代、更安全、更强大的方式重新定义了 Java 字节码操作。随着 Java 生态向模块化、云原生、高性能方向发展,ByteBuddy 成为字节码操作的事实标准已是大势所趋

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐