单个事务中两方法各配独立重试逻辑的实现方案是否正确?
结论:你当前的实现是错误的,无法达到预期效果,核心问题有两个:
1. 同类内部调用导致@Retryable注解完全失效
Spring的@Retryable、@Transactional这类注解都是基于动态代理切面实现的,只有外部调用代理对象的注解方法时,切面逻辑才会生效。你现在在m3()里直接调用同一个类内的m1()、m2(),属于当前对象的本地方法调用,直接绕开了代理对象,两个方法上的@Retryable根本不会触发,没有任何重试效果。
2. 事务与重试的逻辑顺序存在冲突
就算你解决了代理调用的问题,默认配置下也会有逻辑问题:
- Spring事务切面的默认优先级高于重试切面,也就是说事务会开启在重试逻辑的外层,如果
m1或m2抛出异常触发事务回滚规则,当前事务会被直接标记为rollbackOnly,后续就算重试成功,整个事务最终还是会被回滚,重试完全无效。 - 如果你调整优先级让重试运行在事务外层,那每次重试都会开启新的事务,不符合你要求的「两个操作在同一个事务」的需求。
可行实现方案
方案一:手写重试逻辑(最可控,无代理和顺序问题)
直接在事务方法内手写重试逻辑,不需要依赖@Retryable注解,完全符合你的需求:
@Service public class ServiceA { @Autowired private DaoA daoA; @Autowired private DaoB daoB; @Transactional public void m3() { // insertData独立重试5次 int insertRetry = 0; boolean insertOk = false; while (insertRetry < 5 && !insertOk) { try { daoA.insertData(); insertOk = true; } catch (Exception e) { insertRetry++; if (insertRetry >= 5) { throw new RuntimeException("insertData重试5次失败", e); } // 可选:添加重试间隔 // Thread.sleep(100); } } // updateData独立重试5次 int updateRetry = 0; boolean updateOk = false; while (updateRetry < 5 && !updateOk) { try { daoB.updateData(); updateOk = true; } catch (Exception e) { updateRetry++; if (updateRetry >= 5) { throw new RuntimeException("updateData重试5次失败", e); } // 可选:添加重试间隔 // Thread.sleep(100); } } } }
注意:如果数据库抛出的是事务回滚类异常,当前事务会被标记为只能回滚,就算重试成功最终也会回滚。这种场景下你要么只重试非事务回滚类异常,要么放弃同一个事务要求,给两个操作设置独立事务自行保证一致性。
方案二:解决代理调用问题后用@Retryable
如果一定要用@Retryable注解,可以通过自我注入的方式解决内部调用问题,同时调整重试切面优先级确保重试运行在事务内部:
@Service public class ServiceA { @Autowired private DaoA daoA; @Autowired private DaoB daoB; // 自我注入,调用代理对象方法触发切面 @Lazy @Autowired private ServiceA self; @Retryable(maxAttempts = 5, retryFor = Exception.class) public void m1() { daoA.insertData(); } @Retryable(maxAttempts = 5, retryFor = Exception.class) public void m2() { daoB.updateData(); } @Transactional public void m3() { self.m1(); self.m2(); } // 配置重试切面优先级高于事务切面,确保重试运行在事务内部 @Bean public RetryCustomizer retryCustomizer() { return retryTemplate -> retryTemplate.setOrder(Ordered.LOWEST_PRECEDENCE - 2); } }
内容的提问来源于stack exchange,提问作者hello world
相关产品推荐
相关产品推荐

