Java CGLIB代理案例:从入门到企业级应用实战指南
目录导读
CGLIB核心原理与JDK代理区别
CGLIB(Code Generation Library) 通过字节码技术动态生成目标类的子类,并重写非final方法实现代理,与JDK动态代理(仅支持接口)相比,CGLIB可直接代理普通类。

- 核心机制:通过
Enhancer类创建子类,MethodInterceptor拦截方法调用。 - 性能优势:运行时生成字节码,适用于高频调用的场景,但首次生成类较慢。
- 限制条件:无法代理
final类或final方法(子类无法继承)。
示例场景:DAO层无接口实现类、第三方库类(如java.io.FileInputStream)、Spring AOP默认配置(当目标类未实现接口时)。
环境搭建与基础依赖配置
Maven依赖(使用cglib 3.3.0+):
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
Gradle依赖:
implementation 'cglib:cglib:3.3.0'
Spring Boot自动引入:通过spring-boot-starter-aop时,若目标类无接口,会自动切换为CGLIB代理(需检查@EnableAspectJAutoProxy的proxyTargetClass属性)。
实战案例:无接口类动态代理
1 目标类定义(无接口)
public class UserService {
public String getUserName(Long id) {
System.out.println("Executing getUserName...");
return "User_" + id;
}
// final方法无法被代理
public final void printFinal() {
System.out.println("This method is final");
}
}
2 实现MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("【Before】Calling method: " + method.getName());
// 调用父类方法(实际是目标类)
Object result = proxy.invokeSuper(obj, args);
System.out.println("【After】Method executed, result: " + result);
return result;
}
}
3 使用Enhancer生成代理
import net.sf.cglib.proxy.Enhancer;
public class CglibDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // 设置父类(目标类)
enhancer.setCallback(new LogInterceptor()); // 设置回调
UserService proxy = (UserService) enhancer.create();
String name = proxy.getUserName(123L);
// 输出: [Before] Calling method: getUserName
// Executing getUserName...
// [After] Method executed, result: User_123
proxy.printFinal(); // final方法不会被拦截!
}
}
问答专区:高频面试与工程难点
Q1:为什么CGLIB比JDK代理更快?
A:CGLIB使用FastClass机制,直接调用索引定位方法,避免反射调用,但首次生成类需加载字节码,启动稍慢。
Q2:如何强制Spring使用CGLIB代理?
A:在配置类添加:
@EnableAspectJAutoProxy(proxyTargetClass = true)
或XML配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
Q3:CGLIB代理对象为什么不能转换为目标类?
A:因为代理是目标类的子类,类型兼容,但注意:proxy instanceof UserService为true,但不可转换为OtherClass。
Q4:如何解决CGLIB导致的“循环依赖”问题?
A:Spring 5.0后通过早期暴露工厂对象(EarlySingletonReference)解决,也可使用@Lazy注解或调整依赖结构。
Q5:如何调试CGLIB生成的代理类?
A:设置系统属性:
cglib.debugLocation=D:/temp/cglib_classes
JVM参数:
-Dcglib.debugLocation=/tmp/classes
性能调优与最佳实践
1 优化点
- 避免重复创建Enhancer:使用单例模式缓存
Enhancer或Callback(如Spring的ProxyFactory)。 - 使用
FixedValue代替MethodInterceptor:若仅需返回值替换,可降低开销。 - 批量拦截:通过
CallbackFilter为不同方法指定不同拦截器。
2 企业级集成场景
- MyBatis Mapper代理:使用CGLIB为Mapper接口生成实现类(配合
MapperFactoryBean)。 - 事务管理:
@Transactional注解在非接口类上自动启用CGLIB。 - Mock测试框架:如Mockito CGLIB代理模拟类。
3 常见错误与规避
| 错误场景 | 错误原因 | 解决方案 |
|---|---|---|
ClassCastException |
强转为接口类型 | 确保目标类未实现接口,或者使用proxy instanceof UserService判断 |
Method not found |
非public方法被拦截 | CGLIB只能代理public/protected方法,private无效 |
StackOverflow |
拦截器调用自身 | 使用proxy.invokeSuper()而非method.invoke() |
延伸阅读:
- 官方文档:http://cglib.sourceforge.net/apidocs/
- Spring AOP源码分析:
org.springframework.aop.framework.CglibAopProxy - 对比工具:JMH基准测试CGLIB vs JDK代理性能
(全文完)