CGLIB 简介
CGLIB(Code Generation Library)是一个高性能、强大的开源代码生成库,主要用于在运行时动态生成类或增强现有类的功能。它是 Java 动态代理机制的一种实现方式,广泛应用于像 Spring 和 Hibernate 这样的框架中,尤其是在 AOP(面向切面编程)和懒加载等场景下。
1. CGLIB 的特点
-
基于字节码生成:
- CGLIB 是通过字节码操作(ASM 框架)在运行时动态生成类的子类。
- 它不依赖于接口,可以代理普通类,而不像 Java 的 JDK 动态代理必须基于接口。
-
使用 ASM 框架:
- CGLIB 底层依赖 ASM(一个操作字节码的框架),允许对字节码进行直接操作,从而提高了性能。
-
性能高效:
- CGLIB 生成的代理类在运行时的性能优于 JDK 动态代理,因为它是直接生成字节码,而不是通过反射调用。
- 但代理类的生成过程比 JDK 动态代理稍微复杂,初始化会稍慢。
-
不能代理
final修饰的类或方法:- 因为 CGLIB 是通过生成子类来实现代理的,所以对于
final修饰的类或方法,无法继承或重写,这就导致代理失败。
- 因为 CGLIB 是通过生成子类来实现代理的,所以对于
2. CGLIB 的工作原理
2.1 基本概念
CGLIB 的核心思想是通过生成目标类的子类(动态代理类),并在子类中拦截父类方法的调用,以实现增强功能。
-
代理子类:
CGLIB 动态生成的子类继承了目标类,并重写了目标类中的方法。 -
方法拦截器:
CGLIB 使用MethodInterceptor接口来拦截目标方法的调用。Interceptor接口的实现类可以在方法调用前后加入自定义逻辑。 -
字节码生成:
使用 ASM 框架直接生成字节码,避免了反射的性能开销。
2.2 工作流程
CGLIB 的动态代理流程主要包括以下几个步骤:
-
创建一个 Enhancer(增强器)实例:
Enhancer是 CGLIB 的核心类,用于生成代理类。
-
设置目标类:
- 将需要代理的类(目标类)设置到
Enhancer中。
- 将需要代理的类(目标类)设置到
-
设置拦截器:
- 提供一个拦截器实现类(
MethodInterceptor),该拦截器会拦截目标类的方法调用,并在拦截时插入自定义逻辑。
- 提供一个拦截器实现类(
-
生成代理类:
- 调用
create()方法生成目标类的子类(即代理类)。
- 调用
3. CGLIB 的简单示例
以下是一个使用 CGLIB 的简单示例。
3.1 引入依赖
如果使用 Maven 构建项目,可以在 pom.xml 中添加以下依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version> <!-- ASM 库,CGLIB 底层依赖 -->
</dependency>
3.2 代码示例
以下示例演示了如何使用 CGLIB 创建一个动态代理类:
目标类
public class HelloService {
public void sayHello() {
System.out.println("Hello, world!");
}
}
自定义拦截器
实现 MethodInterceptor 接口,拦截目标类的方法调用:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CustomMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
// 调用目标类的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
}
生成代理类
使用 Enhancer 生成目标类的代理对象:
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyExample {
public static void main(String[] args) {
// 创建 Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置目标类
enhancer.setSuperclass(HelloService.class);
// 设置拦截器
enhancer.setCallback(new CustomMethodInterceptor());
// 创建代理对象
HelloService proxy = (HelloService) enhancer.create();
// 调用代理对象的方法
proxy.sayHello();
}
}
运行结果
Before method: sayHello
Hello, world!
After method: sayHello
4. CGLIB 的应用场景
4.1 在 Spring 中的应用
CGLIB 在 Spring 框架中有广泛的应用,主要包括以下场景:
-
AOP(面向切面编程):
- Spring 的 AOP 支持两种动态代理方式:JDK 动态代理和 CGLIB 动态代理。
- 当目标类没有实现接口时,Spring 使用 CGLIB 来生成该类的代理。
-
@Transactional注解:- Spring 使用 CGLIB 动态代理来为带有事务注解的方法添加事务拦截逻辑。
-
Bean 的懒加载:
- Spring 中的
Lazy-Initialization功能利用 CGLIB 动态代理来延迟初始化 Bean。
- Spring 中的
4.2 Hibernate 的懒加载
CGLIB 在 Hibernate 中常用于实现懒加载(Lazy Loading)。当访问某些关联数据(如集合或关系)时,Hibernate 通过 CGLIB 生成代理类,按需加载数据。
5. CGLIB 与 JDK 动态代理的对比
| 特性 | CGLIB 动态代理 | JDK 动态代理 |
|---|---|---|
| 代理目标 | 基于继承,代理类的子类 | 基于接口,代理实现了目标类的接口 |
| 目标类要求 | 不能代理 final 类或 final 方法 | 目标类必须实现接口 |
| 性能 | 运行时性能较高,但生成代理类速度稍慢 | 运行时性能稍低,但生成代理类速度较快 |
| 底层实现 | 使用 ASM 操作字节码 | 使用 Java 内置的反射机制 |
| Spring 中的应用 | 当目标类没有实现接口时,Spring 使用 CGLIB 动态代理 | 当目标类实现了接口时,Spring 使用 JDK 动态代理 |
6. 注意事项
-
final修饰的类或方法无法代理:- 因为 CGLIB 是通过继承目标类实现代理的,所以如果目标类或方法被
final修饰,则无法被代理。
- 因为 CGLIB 是通过继承目标类实现代理的,所以如果目标类或方法被
-
性能开销:
- 虽然 CGLIB 动态代理比 JDK 动态代理的运行时性能更高,但代理类的生成过程稍慢,需要权衡使用场景。
-
内存开销:
- CGLIB 生成的代理类可能会增加 JVM 的 PermGen(永久代)空间使用,可能在长时间运行的应用中引发内存问题(在 Java 8 之后,PermGen 已被
Metaspace替代,问题有所缓解)。
- CGLIB 生成的代理类可能会增加 JVM 的 PermGen(永久代)空间使用,可能在长时间运行的应用中引发内存问题(在 Java 8 之后,PermGen 已被
7. 总结
- CGLIB 是一个高性能的字节码生成库,通过动态生成目标类的子类来实现代理和增强。
- 它广泛应用于 Spring、Hibernate 等框架中,用于提供如 AOP、事务管理以及懒加载等功能。
- 与 JDK 动态代理相比,CGLIB 的优势在于能够代理没有实现接口的普通类,性能也更高,但无法代理
final类和方法。

3114

被折叠的 条评论
为什么被折叠?



