Java中无法直接通过自定义注解自动实现死锁回滚后的补偿重试逻辑,需结合AOP或Spring Retry,在捕获DeadlockLoserDataAccessException时依@RetryOnDeadlock注解参数(maxAttempts、backoffMs)执行带退避的循环重试。

Java 中无法直接通过自定义注解“自动”实现死锁回滚后的补偿重试逻辑——注解本身只是元数据,不包含执行能力。真正起作用的是配合 AOP(面向切面编程)或 Spring Retry 等机制,在捕获到 DeadlockLoserDataAccessException(Spring 封装的 MySQL 死锁异常)时,按注解配置的参数进行重试控制。
自定义注解定义重试策略
定义一个注解,声明最大重试次数和退避延迟(单位毫秒),支持在方法上使用:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOnDeadlock {
int maxAttempts() default 3;
long backoffMs() default 100;
}
该注解不带逻辑,仅用于标记需要死锁重试的方法,并携带配置参数。
用 AOP 拦截并实现重试逻辑
编写切面,在目标方法抛出死锁异常时捕获、休眠、重试,直到达到最大次数:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 使用
@Around切入带@RetryOnDeadlock的方法 - 提取注解参数:
maxAttempts和backoffMs - 用循环 + try-catch 捕获
DeadlockLoserDataAccessException(MySQL 死锁异常的典型 Spring 封装类) - 每次失败后线程休眠指定毫秒数,再重试;成功则直接返回结果;超次则抛出原始异常
示例关键逻辑片段:
for (int i = 0; i <= maxAttempts; i++) {
try {
return joinPoint.proceed(); // 执行原方法
} catch (DeadlockLoserDataAccessException e) {
if (i == maxAttempts) throw e;
Thread.sleep(backoffMs);
}
}
结合 Spring Retry 更简洁可靠
推荐优先使用 Spring Retry(需引入 spring-retry),它原生支持注解驱动、退避策略和监听器:
- 启用重试:在启动类加
@EnableRetry - 将自定义注解与
@Retryable关联,可通过@AliasFor或自定义RetryInterceptor实现参数映射 - 更推荐方式:直接用
@Retryable+@Recover,并在其include中明确指定DeadlockLoserDataAccessException.class - 退避可配置
@ExponentialBackoff或固定延迟delay = 100, maxAttempts = 3
这样既避免手写 AOP 容易遗漏事务传播、异常包装等问题,又天然兼容 Spring 事务上下文。
注意事务与重试的协同
死锁发生在数据库层面,Spring 默认事务遇到异常会回滚,但重试必须在新事务中进行:
- 被重试的方法需声明为
PROPAGATION_REQUIRES_NEW,否则重试仍在已回滚事务中执行,会报 “Transaction already completed” - 若原方法已有事务,重试逻辑应隔离运行,避免污染外层事务状态
- AOP 切面内手动开启新事务较复杂,Spring Retry 在
@Retryable方法上自动支持REQUIRES_NEW配置
不复杂但容易忽略

















