Spring(Spring Boot、Spring Data)下监听事务执行结果的实现方法有哪些?
Spring Data 事务审计实现方案
方案选型结论
- 仅需要记录创建人、创建时间、更新人、更新时间这类基础CRUD字段:直接用Spring Data自带的
@CreatedBy/@CreatedDate/@LastModifiedBy/@LastModifiedDate注解配合@EnableJpaAuditing即可,无需自定义切面 - 需要记录事务成功/回滚状态、失败原因、业务对象完整操作内容:仅需要轻量自定义切面做切点拦截,结合Spring自带的事务同步机制即可实现,不需要从零编写事务生命周期控制逻辑
完整实现示例
1. 定义审计日志实体
@Entity @Table(name = "operation_audit_log") @Data public class OperationAuditLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 操作用户名 private String operator; // 被操作业务对象类型 private String businessObjectType; // 被操作业务对象ID private String businessObjectId; // 操作内容快照 private String operationContent; // 事务状态:SUCCESS/ROLLBACK private String transactionStatus; // 回滚原因(事务失败时填充) private String failReason; // 事件发生时间 private Date operateTime; }
对应的JPA Repository:
public interface OperationAuditLogRepository extends JpaRepository<OperationAuditLog, Long> { }
2. 自定义审计标记注解
用来标记需要审计的事务方法,减少切面匹配范围,降低性能损耗:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TransactionAudit { // 被操作的业务对象类型,比如ORDER/USER String businessType(); }
3. 实现审计切面
核心逻辑是拦截带审计标记的事务方法,收集基础审计信息后,通过Spring的TransactionSynchronizationManager绑定事务生命周期回调,确保审计日志的写入和业务事务状态绑定:
@Aspect @Component public class TransactionAuditAspect { @Autowired private OperationAuditLogRepository auditLogRepository; @Autowired private ObjectMapper objectMapper; // 切点:同时标注了@TransactionAudit和@Transactional的方法 @Pointcut("@annotation(transactionAudit) && @annotation(org.springframework.transaction.annotation.Transactional)") public void auditPointcut(TransactionAudit transactionAudit) {} @Around("auditPointcut(transactionAudit)") public Object around(ProceedingJoinPoint joinPoint, TransactionAudit transactionAudit) throws Throwable { // 预先收集审计基础信息 OperationAuditLog auditLog = new OperationAuditLog(); // 操作用户从Spring Security上下文获取,可根据自身权限体系调整 Authentication auth = SecurityContextHolder.getContext().getAuthentication(); auditLog.setOperator(auth != null ? auth.getName() : "system"); auditLog.setBusinessObjectType(transactionAudit.businessType()); auditLog.setOperateTime(new Date()); // 提取业务方法参数作为操作内容 Object[] args = joinPoint.getArgs(); if (args.length > 0) { auditLog.setOperationContent(objectMapper.writeValueAsString(args[0])); // 可根据自身业务实体基类提取业务对象ID if (args[0] instanceof BaseEntity) { auditLog.setBusinessObjectId(((BaseEntity) args[0]).getId().toString()); } } try { Object result = joinPoint.proceed(); // 注册事务提交成功后的回调 if (TransactionSynchronizationManager.isActualTransactionActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { auditLog.setTransactionStatus("SUCCESS"); auditLogRepository.save(auditLog); } }); } else { // 无事务的场景直接记录 auditLog.setTransactionStatus("SUCCESS"); auditLogRepository.save(auditLog); } return result; } catch (Throwable e) { // 注册事务完成后的回调,判断是否回滚 if (TransactionSynchronizationManager.isActualTransactionActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCompletion(int status) { if (status == STATUS_ROLLED_BACK) { auditLog.setTransactionStatus("ROLLBACK"); auditLog.setFailReason(e.getMessage()); auditLogRepository.save(auditLog); } } }); } else { auditLog.setTransactionStatus("ROLLBACK"); auditLog.setFailReason(e.getMessage()); auditLogRepository.save(auditLog); } throw e; } } }
4. 启用配置
@SpringBootApplication @EnableJpaAuditing @EnableAspectJAutoProxy public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
5. 业务方法使用示例
@Service public class OrderService { @Autowired private OrderRepository orderRepository; @Transactional @TransactionAudit(businessType = "ORDER") public Order createOrder(Order order) { // 业务逻辑实现 return orderRepository.save(order); } }
补充说明
- 审计日志的保存操作是在业务事务结束后执行的,不会和业务事务一起回滚,保证了日志的可靠性
- 如果仅需要做实体变更历史追溯,不需要自定义字段,也可以直接用Spring Data Envers模块,无需编写代码即可自动记录所有实体的变更历史
- 高并发场景下可以先把审计日志写入MQ异步消费落库,减少业务接口的响应耗时
内容的提问来源于stack exchange,提问作者skyho
相关产品推荐
相关产品推荐

