深入理解 CGLIB 代理技术:原理、使用与实战
在 Java 的 AOP(面向切面编程)和动态代理世界里,CGLIB 代理是一个非常重要的技术点。很多初学者在使用 Spring AOP 时可能会注意到:接口没有实现类时,Spring 会使用 CGLIB 代理,而不是 JDK 动态代理。本文将详细讲解 CGLIB 代理,让你从原理到实战都能掌握。
1. 什么是 CGLIB 代理?
CGLIB(Code Generation Library)是一个功能强大的字节码生成库,它可以在运行时动态生成某个类的子类,从而实现方法拦截和增强。
与 JDK 动态代理不同:
-
JDK 动态代理只能代理实现了接口的类。
-
CGLIB 代理可以代理普通的类,即使它没有实现接口。
简而言之,CGLIB 是通过 继承的方式来生成代理对象的。
2. CGLIB 的工作原理
CGLIB 代理的核心是 生成目标类的子类,并在子类中重写目标类的方法,在方法中加入增强逻辑。
原理流程如下:
-
运行时生成子类:CGLIB 根据目标类生成一个新的子类。
-
方法拦截:新生成的子类会重写目标类的方法,在方法入口和出口插入自定义逻辑。
-
调用代理方法:客户端通过代理对象调用方法时,实际上执行的是子类中的方法,并经过拦截器处理。
可以用一句话总结:
CGLIB = “继承 + 方法拦截”。
CGLIB 的核心类是 Enhancer,它负责生成代理类。
3. CGLIB 的使用示例
下面用一个简单的例子演示如何使用 CGLIB 创建代理。
假设有一个普通的业务类:
public class UserService {public void addUser() {System.out.println("添加用户逻辑");}
}
我们希望在方法执行前后添加日志:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibDemo {public static void main(String[] args) {// 创建 Enhancer 对象Enhancer enhancer = new Enhancer();// 设置父类enhancer.setSuperclass(UserService.class);// 设置回调enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("方法执行前日志");Object result = proxy.invokeSuper(obj, args); // 调用原方法System.out.println("方法执行后日志");return result;}});// 创建代理对象UserService proxy = (UserService) enhancer.create();proxy.addUser();}
}
运行结果:
方法执行前日志
添加用户逻辑
方法执行后日志
可以看到,CGLIB 在方法执行前后成功插入了自定义逻辑。
4. CGLIB 的特点与优缺点
特点
-
基于继承:生成子类进行方法增强。
-
无需接口:可以代理普通类。
-
运行时生成字节码:效率高于反射调用,但略低于 JDK 动态代理。
优点
-
支持没有接口的类。
-
可以对目标类的所有方法进行增强。
-
与 Spring AOP 集成良好。
缺点
-
无法代理 final 类和 final 方法:因为继承无法重写 final 方法。
-
内存消耗略高:生成大量类时需要占用 Metaspace。
-
调试稍复杂:生成的代理类在调试时类名不是原类。
5. CGLIB 与 JDK 动态代理的区别
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 是否需要接口 | 必须 | 不需要 |
| 代理方式 | 实现接口 | 继承目标类 |
| 性能 | 稍慢 | 稍快(方法多时优) |
| 适用场景 | 已有接口的类 | 没有接口的类 |
在 Spring 中:
-
如果目标类实现了接口,默认使用 JDK 动态代理。
-
如果目标类没有实现接口,Spring 会自动使用 CGLIB。
6. CGLIB 的实际应用场景
-
Spring AOP:方法拦截、事务增强、日志记录。
-
缓存代理:动态生成代理对象,在方法调用前检查缓存。
-
权限校验:在方法执行前进行权限判断。
例如,Spring 的 @Transactional 注解在没有接口的类上也能生效,就是通过 CGLIB 实现的。
7. 小结
-
CGLIB 是基于 继承 + 方法拦截 的动态代理技术。
-
它可以代理普通类,不依赖接口。
-
适合没有接口或者需要对类中所有方法进行增强的场景。
-
使用时注意 final 类/方法不可代理 和 内存消耗问题。
