CGLIB 代理 vs JDK 代理:能解决哪些事务失效问题?
一、先搞清楚两种代理的核心差异
1.1 JDK 动态代理(默认)
// 原理:基于接口public interface IOrderService { void createOrder(Order order); // 必须是接口}// 代理实现public class OrderServiceProxy implements IOrderService { private IOrderService target; // 持有目标对象 public void createOrder(Order order) { // 1. 开启事务 // 2. 调 target.createOrder(order) // 3. 提交或回滚 }}
JDK 代理限制:
- 只能代理接口方法(public abstract)
- private / protected / final / static 方法 全部代理不到
- 同类内 this 调用也不走代理
1.2 CGLIB 代理(继承式)
// 原理:基于继承public class OrderService { // 不需要接口 public void createOrder(Order order) { ... } private void privateMethod() { ... } // CGLIB 能代理 private}// CGLIB 生成的子类public class OrderService$$EnhancerByCGLIB$$xxx extends OrderService { public void createOrder(Order order) { // 1. 开启事务 // 2. 调 super.createOrder(order) // 3. 提交或回滚 } private void privateMethod() { // ✅ CGLIB 能代理 // 1. 开启事务 // 2. 调 super.privateMethod() }}
CGLIB 优势:
- 不需要接口,直接继承类
- 能代理 private / protected 方法(生成子类重写)
- 能代理 package-private 方法
CGLIB 限制:
- 不能代理 final 方法(不能重写 final)
- 不能代理 static 方法(属于类,不属于实例)
- 同类内 this 调用仍然不走代理(super 调用绕过代理对象)
二、CGLIB 能解决哪些事务失效问题?
2.1 CGLIB 能解决的(主要是方法可见性问题)
| 场景 | JDK 代理 | CGLIB 代理 | 解决? |
|---|---|---|---|
| private 方法 | ❌ 代理不到 | ✅ 能代理 | ✅ |
| protected 方法 | ❌ 代理不到 | ✅ 能代理 | ✅ |
| package-private 方法 | ❌ 代理不到 | ✅ 能代理 | ✅ |
| final 方法 | ❌ 代理不到 | ❌ 也不能代理(CGLIB 不能重写 final) | ❌ |
| static 方法 | ❌ 代理不到 | ❌ 也不能代理 | ❌ |
2.2 CGLIB 解决不了的(事务失效核心问题)
| 场景 | JDK 代理 | CGLIB 代理 | 解决? |
|---|---|---|---|
| 异常自己 catch 住 | ❌ 失效 | ❌ 失效 | ❌ CGLIB 也救不了 |
| 抛检查异常没配 rollbackFor | ❌ 失效 | ❌ 失效 | ❌ |
| 同类 this 调用 | ❌ 失效 | ❌ 失效 | ❌ |
| @Transactional 在接口上 | ❌ 失效 | ⚠️ 部分解决(直接代理实现类) | ⚠️ |
| @Async 异步 | ❌ 失效 | ❌ 失效 | ❌ |
| 多线程调用 | ❌ 失效 | ❌ 失效 | ❌ |
| AOP 顺序 | ❌ 失效 | ❌ 失效 | ❌ |
核心结论:
- CGLIB 只解决"方法可见性"问题(private / protected)
- 事务失效的 80% 问题是"异常处理"和"调用方式",CGLIB 帮不了
三、Spring 怎么配置 CGLIB 代理?
3.1 Spring Boot 2.x+(老哥项目一般用这个)
@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true) // ✅ 强制 CGLIBpublic class AppConfig {}
或者 application.yml:
spring: aop: proxy-target-class: true # ✅ Spring Boot 配置
Spring Boot 2.x 默认就是 CGLIB 代理!(Spring Boot 1.x 是 JDK)
3.2 验证当前项目是不是 CGLIB
@SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args); OrderService bean = ctx.getBean(OrderService.class); System.out.println(bean.getClass().getName()); // 输出:com.niejq.demo.OrderService$$EnhancerByCGLIB$$xxx // ✅ CGLIB 代理 }}
3.3 老哥项目验证
老哥你的赢时胜 mpvs 项目:Spring Cloud + Spring Boot 2.x → 默认 CGLIB 代理。 老哥你的 DLQ Demo:Spring Boot 3.2.5 → 默认 CGLIB 代理。
所以老哥你项目里 @Transactional 默认就在 CGLIB 代理上跑,private 方法理论上能代理(但业务里很少写 private 事务方法)。
四、CGLIB 解决不了的事务失效,怎么破?
4.1 异常被吃 → 手动回滚
@Servicepublic class OrderService { @Transactional public void createOrder(Order order) { try { orderDao.insert(order); accountDao.debit(order.getAccountId(), order.getAmount()); } catch (Exception e) { // 手动回滚(不管 JDK 还是 CGLIB 都得这么干) TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw new BusinessException("订单创建失败", e); } }}
4.2 检查异常 → rollbackFor
@Transactional(rollbackFor = Exception.class) // 不管什么代理都得配public void createOrder(Order order) throws IOException { // ...}
4.3 this 调用 → 注入 self
@Servicepublic class OrderService { @Autowired private OrderService self; // 注入自己 public void createOrder(Order order) { self.updateOrder(order); // ✅ 走代理(CGLIB 也得这么干) } @Transactional public void updateOrder(Order order) { // CGLIB 也会拦截到 }}
4.4 @Async 失效 → 拆成两个 Bean
@Servicepublic class OrderService { @Autowired private AsyncTaskService asyncTask; // 拆出去 public void createOrder(Order order) { asyncTask.doAsync(order); // ✅ 异步任务是另一个 Bean,走代理 }}@Servicepublic class AsyncTaskService { @Async @Transactional public void doAsync(Order order) { orderDao.insert(order); }}
五、CGLIB vs JDK 代理完整对比
| 维度 | JDK 代理 | CGLIB 代理 |
|---|---|---|
| 实现方式 | 接口 | 继承类 |
| 需要接口 | ✅ 必须 | ❌ 不需要 |
| private 方法 | ❌ 代理不到 | ✅ 能代理 |
| protected 方法 | ❌ 代理不到 | ✅ 能代理 |
| final 方法 | ❌ 代理不到 | ❌ 不能重写 |
| static 方法 | ❌ 代理不到 | ❌ 不能拦截 |
| 同类 this 调用 | ❌ 不走代理 | ❌ 不走代理 |
| 性能 | 略快(反射) | 略慢(生成子类) |
| Spring Boot 2.x+ 默认 | ❌ 默认 CGLIB | ✅ 默认 CGLIB |
| 启动时间 | 快 | 略慢(生成代理类) |
六、面试官追问应对(老哥 30 秒杀手锏)
追问:Spring 默认是 JDK 代理还是 CGLIB 代理?
"Spring Boot 2.x+ 默认 CGLIB 代理,因为现在大部分类没有接口(Spring 推荐面向对象而非接口编程)。
Spring 1.x / 传统 Spring 是 JDK 代理(基于接口),如果项目有接口会用 JDK,没有就用 CGLIB。
强制 CGLIB 代理:
@EnableAspectJAutoProxy(proxyTargetClass = true)或spring.aop.proxy-target-class: true"
追问:CGLIB 代理能解决事务失效的所有问题吗?
"不能。CGLIB 只解决'方法可见性'问题(private / protected),事务失效的核心 3 大原因(异常被吃、检查异常、this 调用)CGLIB 都救不了。
解决方式:
1.异常被吃 → 手动
setRollbackOnly()或抛出2.检查异常 →
rollbackFor = Exception.class3.this 调用 → 注入 self
**CGLIB 只是工具,事务传播规则是根本,**不要迷信配置代理方式能解决所有问题。"
追问:怎么知道当前 Bean 是 JDK 还是 CGLIB 代理?
OrderService bean = ctx.getBean(OrderService.class);System.out.println(bean.getClass().getName());// JDK 代理:com.sun.proxy.$Proxy123// CGLIB 代理:com.niejq.demo.OrderService$$EnhancerByCGLIB$$xxx
记忆口诀
"CGLIB 解决 private,救不了异常吃了"
"JDK 代理只能 public,CGLIB 代理能 private,但 final/static 都不行"
"this 调用 JDK 和 CGLIB 都不走代理,注入 self 才安全"
"Spring Boot 2.x 默认 CGLIB 代理,proxy-target-class 强制开启"
"事务失效改代码,不要迷信改代理"
更多推荐

所有评论(0)