写在文章开头
Spring Boot
为我提供了一个强大的注解TransactionalEventListener
,通过该注解我们可以感知到因为失败而回滚的事务,进行进一步的操作,所以对于某些需要保证事务可靠性或者需要对失败事务进行监控的场景,该注解是非常的实用,下面笔者就会以一个简单的保存接口演示一下事务监听的实用。
你好,我叫sharkchili,目前还是在一线奋斗的Java开发,经历过很多有意思的项目,也写过很多有意思的文章,是CSDN Java领域的博客专家,也是Java Guide的维护者之一,非常欢迎你关注我的公众号:写代码的SharkChili,实时获取笔者最新的技术推文同时还能和笔者进行深入交流。
事务监听基础示例
整体流程
如下所示,我们的controller
接口会提交一个保存的请求,交由带有事务的service
处理,一旦controller
感知到service
错误,在service
回滚事务之后,主动将当前保存的信息发布出去,交由监听器处理。
封装事件
了解整体工作流程之后,我们就可以开始编码,由上可知,我们感知失败事务时要发送错误消息,所以我们封装一个事件对象,记录保存失败的TData 。
kotlin
/**
* tdata事件,记录tdata的信息
*/
public class TransactionEvent {
private TData data;
public TransactionEvent(TData data) {
this.data = data;
}
public TData getData() {
return data;
}
}
为了文章的完整性我们给出TData
的代码
kotlin
@Data
public class TData {
private Integer id;
private String data;
private Byte type;
}
发布异常
完成事件编写之后,我们就需要事件发布事务失败事件这一步,所以我们要编写一个事件发布者,如下所示,可以看到笔者注入Spring
的事件发布工具,通过eventPublisher
来发布我们的事务失败事件TransactionEvent
。
typescript
@Component
public class TransactionEventPublisher {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void publishEvent(TransactionEvent event) {
eventPublisher.publishEvent(event);
}
}
感知事件
重点来了,我们封装了一个TDataTransactionalEventListener
用于感知失败的事务事件,可以看到笔者实用TransactionalEventListener
注解,让当前监听器感知回滚的事务事件,并获取当前失败的事务监听的事件内容,在进行输出打印(模拟事件处理):
less
@Component
@Slf4j
public class TDataTransactionalEventListener {
//事务回滚后 对应seqId会被消耗掉
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK, fallbackExecution = true)
public void handleRollbackEvent(TransactionEvent event) {
TData data = event.getData();
log.info("处理回滚事件,data:{}", JSONUtil.toJsonStr(data));
// 执行其他异常处理逻辑
}
}
测试
我们这里编写一个简单的service
模拟事务保存异常:
ini
@Transactional
public int saveData(TData tData) {
int count = tDataMapper.insert(tData);
int i = 1 / 0;
return count;
}
对应的Controller
层,设置try-catch
异常捕获,感知事务失败的错误后,发布一个错误事件:
less
@RestController("/test")
@Slf4j
public class TestController {
@Resource
private TDataService tDataService;
@Autowired
private TransactionEventPublisher transactionEventPublisher;
@PostMapping("save")
public int saveData(@RequestBody TData tData) {
int count;
try {
return tDataService.saveData(tData);
} catch (Exception e) {
log.error("data保存失败,失败原因:{}", e.getMessage(), e);
transactionEventPublisher.publishEvent(new TransactionEvent(tData));
return 0;
}
}
}
对应输出结果如下,可以看到发布事务监听事件之后,监听器即可获取到本次失败的数据内容并进行进一步的处理。
kotlin
2024-02-12 11:29:28.922 ERROR 9716 --- [io-18080-exec-3] c.sharkChili.controller.TestController : data保存失败,失败原因:/ by zero
java.lang.ArithmeticException: / by zero
2024-02-12 11:29:28.924 WARN 9716 --- [io-18080-exec-3] actionalApplicationListenerMethodAdapter : Processing org.springframework.context.PayloadApplicationEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@182038d5, started on Mon Feb 12 11:29:07 CST 2024] as a fallback execution on AFTER_ROLLBACK phase
2024-02-12 11:29:28.960 INFO 9716 --- [io-18080-exec-3] c.s.e.TDataTransactionalEventListener : 处理回滚事件,data:{"data":"demoData","type":1}
优化
事务监听很适用于业务重试、失败补偿的场景,所以我们这里通过AOP
将其进行封装:
java
Aspect
@Component
@Slf4j
public class TDataAspect {
@Autowired
private TransactionEventPublisher transactionEventPublisher;
/**
* 定义一个切点
*/
@Pointcut("execution(public * com.sharkChili.controller..*Controller.*(..))")
public void transactionPointcut() {
}
@Around("transactionPointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
Object[] args = proceedingJoinPoint.getArgs();
transactionEventPublisher.publishEvent(new TransactionEvent((TData) args[0]));
result = 0;
}
// 排除字段,敏感字段或太长的字段不显示
return result;
}
}
这样Controller
的业务代码就和事务监听解耦
less
@PostMapping("save")
public int saveData(@RequestBody TData tData) throws Exception {
int count;
try {
return tDataService.saveData(tData);
} catch (Exception e) {
log.error("data保存失败,失败原因:{}", e.getMessage(), e);
throw e;
}
}
小结
以上便是笔者对于事务监听的日常实用姿势,整体来说事务监听的开发步骤整体如下:
- 声明事件对象。
- 封装事件发布者。
- 基于
TransactionalEventListener
完成事务监听逻辑封装。 - 编写切面以捕获需要进行事务监听的业务代码,通过
catch
捕获异常,并发布事件。 - 事务监听器完成处理。
我是sharkchili ,CSDN Java 领域博客专家 ,开源项目---JavaGuide contributor ,我想写一些有意思的东西,希望对你有帮助,如果你想实时收到我写的硬核的文章也欢迎你关注我的公众号: 写代码的SharkChili ,同时我的公众号也有我精心整理的并发编程 、JVM 、MySQL数据库个人专栏导航。