You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:18:03