这是一个在 Java 开发中非常重要且实用的技术,尤其与 Spring AOP 等框架紧密相关。

什么是动态代理?
在深入 CGLIB 之前,我们先理解“动态代理”这个概念。
代理模式 是一种设计模式,它为其他对象提供一种代理以控制对这个对象的访问,就是创建一个“替身”对象,这个替身对象可以在不改变原始对象代码的情况下,为原始对象的功能添加额外的操作(如日志、事务、权限校验等)。
动态代理 是指代理类的字节码在程序运行时动态生成,而不是在编译时就确定,这意味着我们可以在运行时决定为哪个接口或类创建代理,以及代理要执行哪些额外逻辑。
Java 官方提供了两种动态代理机制:
- JDK 动态代理:基于接口实现,它只能为实现了接口的类创建代理。
- CGLIB 动态代理:基于类继承实现,它可以为目标类创建一个子类,并重写其非
final、非private的方法,从而实现代理。
为什么需要 CGLIB?
JDK 动态代理虽然方便,但它有一个致命的缺点:它只能代理接口,如果一个类没有实现任何接口,我们就无法使用 java.lang.reflect.Proxy 为其创建代理。
这时,CGLIB (Code Generation Library) 就派上用场了,CGLIB 是一个强大的、高性能的代码生成库,它可以在运行时动态生成一个类的子类,CGLIB 可以代理那些没有实现任何接口的普通类。
核心区别:
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
| :--- | :--- | :--- |
| 实现原理 | 基于接口,实现 InvocationHandler 接口 | 基于继承,生成目标类的子类 |
| 代理对象 | 代理的是接口,proxy.getClass().getInterfaces() 能获取到接口 | 代理的是类,proxy.getClass().getSuperclass() 能获取到目标类 |
| 目标要求 | 目标类必须至少实现一个接口 | 目标类可以是任意普通类(非 final) |
| 性能 | 相对较低 | 相对较高(生成代理类后,方法调用效率高) |
| 局限性 | 无法代理 final 类和 final 方法 | 无法代理 final 类和 final 方法 |
CGLIB 的工作原理
CGLIB 的工作流程如下:
- 创建 Enhancer 对象:
Enhancer是 CGLIB 的核心类,用于生成代理类。 - 设置父类:通过
setSuperclass()方法,指定要代理的目标类。 - 设置回调方法:通过
setCallback()或setCallbacks()方法,指定一个或多个MethodInterceptor。MethodInterceptor是一个拦截器接口,它会在目标方法被调用时执行。 - 创建代理对象:调用
Enhancer的create()方法,CGLIB 在底层会:- 动态生成一个目标类的子类。
- 在这个子类中,它会重写所有非
final、非private的方法。 - 当调用这些重写的方法时,会转而调用
MethodInterceptor的intercept()方法。
- 方法拦截:当客户端调用代理对象的方法时,实际执行的是
MethodInterceptor的intercept()方法,在这个方法中,我们可以决定:- 不执行目标方法:直接返回一个自定义值。
- 执行目标方法:通过
methodProxy.invokeSuper()或method.invoke()调用原始目标类的方法。 - 在执行前后添加逻辑:这是最常见的情况,比如添加日志、开启事务等。
CGLIB 实战示例
让我们通过一个具体的例子来理解 CGLIB 的使用。
1 添加 Maven 依赖
确保你的项目中包含 CGLIB 依赖,如果你使用 Spring Boot,它通常已经包含了,否则,可以手动添加:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 使用较新版本 -->
</dependency>
2 创建目标类
我们创建一个没有任何接口的普通类 UserService。
// UserService.java
package com.example.demo;
public class UserService {
public void addUser(String username) {
System.out.println("正在添加用户: " + username);
// 模拟添加用户的业务逻辑
}
public void deleteUser(Long id) {
System.out.println("正在删除用户 ID: " + id);
// 模拟删除用户的业务逻辑
}
}
3 创建 MethodInterceptor 拦截器
这是 CGLIB 的核心,我们在这里添加额外的逻辑。
// LogInterceptor.java
package com.example.demo;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
/**
* @param o 代理对象,即目标类的子类实例
* @param method 目标方法对象
* @param args 目标方法的参数
* @param methodProxy 代理方法对象,用于调用父类(即目标类)的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("----- CGLIB 动态代理: 方法 [" + method.getName() + "] 开始执行 -----");
// 调用目标类(父类)的方法
// 使用 methodProxy.invokeSuper() 是最高效的方式
Object result = methodProxy.invokeSuper(o, args);
// 如果使用 method.invoke(o, args) 也可以,但效率稍低,且可能引发问题(如递归调用)
// Object result = method.invoke(o, args);
System.out.println("----- CGLIB 动态代理: 方法 [" + method.getName() + "] 执行结束 -----");
return result;
}
}
4 创建并使用代理对象
我们编写一个主类来创建和使用 CGLIB 代理。
// Main.java
package com.example.demo;
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
// 1. 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 2. 设置目标类(父类)
enhancer.setSuperclass(UserService.class);
// 3. 设置回调(拦截器)
enhancer.setCallback(new LogInterceptor());
// 4. 创建代理对象
UserService userServiceProxy = (UserService) enhancer.create();
// 5. 通过代理对象调用方法
System.out.println("代理对象的实际类型: " + userServiceProxy.getClass().getName());
System.out.println("它的父类是: " + userServiceProxy.getClass().getSuperclass().getName());
System.out.println("\n--- 调用代理对象的方法 ---");
userServiceProxy.addUser("张三");
userServiceProxy.deleteUser(123L);
}
}
5 运行结果
代理对象的实际类型: com.example.demo.UserService$$EnhancerByCGLIB$$b1f2d8c3
它的父类是: com.example.demo.UserService
--- 调用代理对象的方法 ---
----- CGLIB 动态代理: 方法 [addUser] 开始执行 -----
正在添加用户: 张三
----- CGLIB 动态代理: 方法 [addUser] 执行结束 -----
----- CGLIB 动态代理: 方法 [deleteUser] 开始执行 -----
正在删除用户 ID: 123
----- CGLIB 动态代理: 方法 [deleteUser] 执行结束 -----
从结果可以看出:
- 代理对象
userServiceProxy的类型是UserService$$EnhancerByCGLIB$$...,这是一个由 CGLIB 动态生成的子类。 - 当我们调用
addUser方法时,并没有直接执行UserService中的代码,而是先进入了LogInterceptor的intercept方法,打印了日志,然后通过methodProxy.invokeSuper()执行了原始方法,执行完毕后又打印了日志。
CGLIB 与 Spring AOP
在 Spring 框架中,AOP(面向切面编程)的实现就大量使用了 CGLIB。
- 默认策略:如果你的目标类实现了至少一个接口,Spring 默认会使用 JDK 动态代理 来创建 AOP 代理。
- 降级策略:如果你的目标类没有实现任何接口,Spring 会自动降级使用 CGLIB 来创建代理。
你可以通过配置强制 Spring 使用 CGLIB:
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB
public class AppConfig {
// ...
}
Spring AOP 的 @Around、@Before 等注解,其底层原理就是通过类似 CGLIB 的 MethodInterceptor 来实现的,当你定义一个切面时,Spring 会创建一个代理对象,并在你定义的切点(Pointcut)匹配到方法时,执行你的通知逻辑。
CGLIB 的注意事项
- 无法代理
final类:因为 CGLIB 使用继承,而 Java 不允许继承final类。 - 无法代理
final方法:因为final方法不能被子类重写,CGLIB 无法拦截其调用。 - 构造函数调用问题:在 CGLIB 代理对象的构造函数中,目标类的构造函数会被调用两次,一次是在生成代理类字节码时,另一次是在创建代理实例时,这可能会对某些依赖构造函数逻辑的代码产生影响。
- 性能:虽然 CGLIB 的方法调用效率比 JDK 代理高,但生成代理类的过程本身比 JDK 代理要慢,它不适合为大量短生命周期的对象创建代理。
- 类加载器问题:在复杂的应用(如 OSGi)或某些特殊环境中,类加载器可能会导致问题。
| 特性 | |
|---|---|
| 核心作用 | 为没有实现接口的 Java 类提供动态代理能力。 |
| 实现原理 | 通过字节码技术,在运行时动态生成目标类的子类,并重写其方法。 |
| 核心组件 | Enhancer(生成器)、MethodInterceptor(拦截器)。 |
| 关键方法 | Enhancer.create()、MethodInterceptor.intercept()、MethodProxy.invokeSuper()。 |
| 应用场景 | Spring AOP 的默认代理方式之一、Mock 测试(如 Mockito)、RPC 框架等。 |
| 优缺点 | 优点:代理类,适用范围广,性能高。缺点:不能代理 final 类/方法,生成代理慢,构造函数可能被调用两次。 |
理解 CGLIB 不仅能让你更深入地掌握 Java 动态代理,也能帮助你更好地理解像 Spring 这样的框架是如何工作的。
