核心定义
CGLIB (Code Generation Library) 是一个强大的、高性能的第三方代码生成库。它被广泛用于在运行时扩展 Java 类和实现 AOP。与 JDK 动态代理不同,CGLIB 的代理机制是基于继承的。
当 Spring AOP 发现一个需要被代理的目标对象没有实现任何接口时,它就会自动切换到 CGLIB 来创建代理。
工作原理
CGLIB 的工作原理是基于字节码增强 (Bytecode Enhancement) 技术,其核心是 net.sf.cglib.proxy.Enhancer
类和 net.sf.cglib.proxy.MethodInterceptor
接口。
它的工作流程可以分解为以下步骤:
1. 前提:定义目标类 (无需接口)
CGLIB 的强大之处在于,你的目标类可以是一个普通的 Java 类 (POJO),不需要实现任何接口。
// 1. 目标类 (可以没有接口)
public class UserService {
public void addUser(String username) {
System.out.println("【核心业务】: 添加用户 " + username);
}
// 一个 final 方法
public final void deleteUser(String username) {
System.out.println("【核心业务】: 这是一个 final 方法,无法被代理!");
}
}
2. 编写方法拦截器 (MethodInterceptor)
你需要创建一个实现了 MethodInterceptor
接口的类。这个类等同于 JDK 代理中的 InvocationHandler
,是所有增强逻辑的集中地。所有对代理对象方法的调用都会被这个拦截器的 intercept
方法捕获。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 2. 编写方法拦截器,这是所有增强逻辑的所在地
public class MyMethodInterceptor implements MethodInterceptor {
/**
* @param obj 代理对象本身
* @param method 被拦截的方法
* @param args 方法参数
* @param proxy 用于调用父类(即目标类)的方法,性能比反射高
* @return 方法返回值
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// --- 前置增强 ---
System.out.println("【前置通知】: " + method.getName() + " 方法执行前,进行权限检查...");
// 调用父类(即原始目标类)的方法
// 注意:这里推荐使用 proxy.invokeSuper(obj, args) 而不是 method.invoke(target, args)
// 因为 invokeSuper 避免了反射,性能更好。
Object result = proxy.invokeSuper(obj, args);
// --- 后置增强 ---
System.out.println("【后置通知】: " + method.getName() + " 方法执行后,记录日志...");
return result;
}
}
3. 生成并使用代理对象
最后,使用 Enhancer
类来动态地创建代理对象。
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 1. 创建 Enhancer 对象,类似于 JDK 代理的 Proxy 类
Enhancer enhancer = new Enhancer();
// 2. 设置父类(目标类)
// CGLIB 将会动态生成这个父类的子类
enhancer.setSuperclass(clazz);
// 3. 设置回调函数(我们写的方法拦截器)
enhancer.setCallback(new MyMethodInterceptor());
// 4. 创建代理对象
return enhancer.create();
}
}
// --- 客户端使用 ---
public class Main {
public static void main(String[] args) {
// 获取代理对象
UserService proxy = (UserService) CglibProxyFactory.getProxy(UserService.class);
System.out.println("--- 调用 addUser ---");
proxy.addUser("Alice");
System.out.println("\n--- 调用 deleteUser (final 方法) ---");
proxy.deleteUser("Bob"); // 注意观察这个方法的调用结果
// 代理对象的真实类型是什么?
System.out.println("\n代理对象的类型: " + proxy.getClass().getName());
}
}
运行结果:
--- 调用 addUser ---
【前置通知】: addUser 方法执行前,进行权限检查...
【核心业务】: 添加用户 Alice
【后置通知】: addUser 方法执行后,记录日志...
--- 调用 deleteUser (final 方法) ---
【核心业务】: 这是一个 final 方法,无法被代理!
代理对象的类型: com.example.UserService$$EnhancerByCGLIB$$...
从结果可以看出:
addUser
方法被成功代理,前后置逻辑被执行。deleteUser
方法因为是final
的,CGLIB 无法重写它,所以代理逻辑没有生效,直接调用了原始方法。- 代理对象的类型是
UserService
的一个动态生成的子类,名字中包含了EnhancerByCGLIB
标识。
限制与缺点
CGLIB 虽然强大,但它的工作原理(继承)也带来了自身的限制:
-
无法代理
final
类- 因为代理类需要继承目标类,而 Java 的语法规定
final
类是不能被继承的。所以,如果你的业务类被声明为final
,CGLIB 将无法为它创建代理,会抛出异常。
- 因为代理类需要继承目标类,而 Java 的语法规定
-
无法代理
final
、private
或static
方法final
方法:子类无法重写(Override)父类的final
方法,所以 CGLIB 的拦截器对它无能为力。private
方法:子类无法访问父类的private
方法,自然也无法重写。static
方法:静态方法属于类而不是对象,不能被重写。- 因此,AOP 的增强逻辑对这些方法是无效的。
-
构造函数会被调用两次(在某些旧版本中)
- 在创建代理对象时,需要先创建父类(目标类)的实例,然后再创建子类(代理类)的实例,这可能导致目标类的构造函数被调用两次。不过在现代版本中这个问题已经得到了优化。
-
需要引入第三方库
- 与 JDK 动态代理不同,CGLIB 不是 Java 的原生部分,需要额外引入
cglib.jar
包(不过像 Spring Boot 这样的框架已经自动帮你管理好了)。
- 与 JDK 动态代理不同,CGLIB 不是 Java 的原生部分,需要额外引入
总结
特性 | 描述 |
---|---|
核心技术 | 字节码增强 (net.sf.cglib.proxy.Enhancer ) |
代理方式 | 在运行时动态地继承目标类,创建其子类作为代理。 |
最大限制 | 无法代理 final 类和 final 、private 、static 方法。 |
优点 | 无需接口即可代理,适用性更广;性能通常被认为略优于 JDK 代理。 |
缺点 | 存在 final 限制;需要引入第三方库。 |