在 Java 编程中,代理技术是一种强大的手段,广泛应用于日志记录、权限控制、事务管理等场景。Java 提供了两种主要的代理方式:JDK 动态代理和 CGLIB 动态代理。JDK 动态代理基于接口,而 CGLIB 动态代理则基于子类继承,可以对没有接口的类进行代理。
本文将深入探讨 CGLIB 的工作原理、核心类、使用方法,并通过实战示例展示其强大能力。
CGLIB 是一个功能强大的字节码生成库,它可以在运行时动态生成目标类的子类,并重写其方法以添加增强逻辑。与 JDK 的基于接口的动态代理不同,CGLIB 可以代理没有实现任何接口的普通类。
✅ 本质上,CGLIB 是对 ASM 字节码操作库的封装,它隐藏了大量复杂的字节码操作细节,开发者只需关注业务逻辑增强即可。
代理类 = 目标类 + 回调逻辑(MethodInterceptor)
代理流程:
目标类 ——> 创建子类(继承) ——> 重写方法 ——> 方法中调用拦截器 ——> 执行增强逻辑 + 原始方法
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
是否基于接口 | ✅ 需要实现接口 | ❌ 不需要接口 |
代理方式 | 接口代理 | 继承代理(子类) |
性能 | 方法调用慢,反射实现 | 更快,基于字节码 |
限制 | 仅能代理接口方法 | final 类或方法无法代理 |
使用场景 | 常用于微服务接口代理 | 常用于框架内部增强(如 Spring) |
CGLIB 的核心在于:通过生成目标类的子类,并重写非 final 的方法,在方法中织入增强逻辑。
原理步骤如下:
类名 | 功能描述 |
---|---|
net.sf.cglib.proxy.Enhancer |
CGLIB 的核心类,用于创建代理对象 |
net.sf.cglib.proxy.MethodInterceptor |
方法拦截器接口,相当于 InvocationHandler |
net.sf.cglib.proxy.MethodProxy |
提供对被代理方法的调用 |
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
public class UserService {
public void createUser(String name) {
System.out.println("创建用户: " + name);
}
public String findUser(int id) {
return "用户#" + id;
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LoggingInterceptor implements 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;
}
}
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置父类
enhancer.setCallback(new LoggingInterceptor()); // 设置回调
UserService proxy = (UserService) enhancer.create();
proxy.createUser("张三");
System.out.println(proxy.findUser(101));
}
}
[日志] 方法开始:createUser
创建用户: 张三
[日志] 方法结束:createUser
[日志] 方法开始:findUser
[日志] 方法结束:findUser
用户#101
Spring AOP 支持 JDK 动态代理和 CGLIB 动态代理:
@EnableAspectJAutoProxy(proxyTargetClass = true)
Spring 使用 CGLIB 创建代理对象的核心流程:
Enhancer
实例,设置父类为目标类;Callback
为 DynamicAdvisedInterceptor
;create()
创建代理类;intercept()
中由 MethodInterceptor
实现。CGLIB 支持多种方法拦截器:
MethodInterceptor
:核心拦截接口。CallbackFilter
:对不同方法使用不同的 Callback。NoOp
:对某些方法不进行增强处理。示例:只拦截 createUser 方法,跳过 deleteUser。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallbacks(new Callback[]{
new LoggingInterceptor(), // 对应 index=0
NoOp.INSTANCE // index=1,不拦截
});
enhancer.setCallbackFilter(method -> method.getName().equals("createUser") ? 0 : 1);
intercept
方法中如果调用自身方法要使用 proxy.invokeSuper()
避免栈溢出。场景 | 描述 |
---|---|
Spring AOP | 在目标类没有接口时,Spring 默认使用 CGLIB 进行代理。 |
数据库 ORM 框架 | 如 Hibernate,使用 CGLIB 实现懒加载(延迟代理)。 |
缓存封装 | 通过方法拦截缓存结果(如基于方法名和参数生成缓存 Key)。 |
性能监控、日志收集 | 实现对关键业务方法调用的埋点监控和日志追踪。 |
CGLIB 是基于继承和字节码技术的强大代理工具,能够在不依赖接口的情况下实现运行时增强。它在 Spring、MyBatis、Hibernate 等框架中发挥着关键作用。
在了解了 JDK 动态代理后,掌握 CGLIB 是深入理解 Java 动态增强机制和 Spring 框架内部运行逻辑的关键一步。