如下所示,我们的controller接口会提交一个保存的请求,交由带有事务的service处理,一旦controller感知到service错误,在service回滚事务之后,主动将当前保存的信息发布出去,交由监听器处理。
/** * tdata事件,记录tdata的信息 */ public class TransactionEvent { private TData data; public TransactionEvent(TData data) { this.data = data; } public TData getData() { return data; } }为了文章的完整性我们给出TData的代码
@Data public class TData { private Integer id; private String data; private Byte type; }发布异常
@Component public class TransactionEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishEvent(TransactionEvent event) { eventPublisher.publishEvent(event); } }感知事件
@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)); // 执行其他异常处理逻辑 } }测试
@Transactional public int saveData(TData tData) { int count = tDataMapper.insert(tData); int i = 1 / 0; return count; }对应的Controller层,设置try-catch异常捕获,感知事务失败的错误后,发布一个错误事件:
@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; } } }对应输出结果如下,可以看到发布事务监听事件之后,监听器即可获取到本次失败的数据内容并进行进一步的处理。
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}优化
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的业务代码就和事务监听解耦
@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; } }小结