java 自动补偿 方法级别的补偿

该博客介绍了如何实现自动补偿和异常重试机制。通过注解、反射和定时任务,当方法调用失败时,系统会自动进行重试。如果重试达到预设次数后仍然失败,则通过钉钉通知开发者。设计包括补偿表的数据库结构,注解定义,切面类处理,以及补偿服务的实现,包括定时任务调度补偿数据和人工重试的逻辑。此外,还提供了一个用于处理补偿数据的辅助类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自动补偿实现

  1. 要求: 方法调用的过程中。失败的时候,系统有办法进行自动重试,重试达到一定次数后,钉钉通知开发
  2. 实现设计: 注解,反射,定时任务
    在这里插入图片描述

表的设计

– 补偿表

CREATE TABLE `a_compensation` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '数据库主键',
  `busKey` VARCHAR(255) NOT NULL COMMENT '业务主键',
  `type` VARCHAR(20) NOT NULL DEFAULT 'auto' COMMENT '处理方式:自动(auto),manual(人工介入)',
  `dataStatus` TINYTEXT NOT NULL COMMENT '数据状态:0待处理,1:成功,2:失败',
  `className` VARCHAR(200) NOT NULL COMMENT '完整类名',
  `beanName` VARCHAR(100) NOT NULL COMMENT '类名',
  `methodName` VARCHAR(100) NOT NULL COMMENT '方法名',
  `reqArgsType` VARCHAR(500) NOT NULL COMMENT '方法入参类型',
  `reqArgs` TEXT NOT NULL COMMENT '方法入参参数',
  `reqArgsTypeReal` VARCHAR(500) DEFAULT NULL COMMENT '实际参数类型',
  `retryCount` BIGINT(10) NOT NULL COMMENT '重试次数',
  `resultMsg` VARCHAR(2000) DEFAULT NULL COMMENT '返回信息',
  `gmtCreate` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
  `gmtModified` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '更新时间',
  `isValid` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '是否有效',
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

代码

  • 注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Compensate {
   
	/**
	 * 异常类型,用于补偿指定异常的数据,默认值Exception
	 */
	Class<?>[] exceptionType() default Exception.class;

}
  • 切面类
@Aspect
@Component
public class AnnotationCompensateEntry {
   
    private static final Logger logger = LoggerFactory.getLogger(AnnotationCompensateEntry.class);
    @Autowired
    private CompensationService compensationService;

    /**
     * 切点,通过注解的方式
     *
     * @param joinPoint 连接点
     * @param e         异常
     */
    @AfterThrowing(pointcut = "@annotation(com.base.annotation.Compensate)", throwing = "e")
    public void aopCompensate(JoinPoint joinPoint, Exception e) {
   
        try {
   
            // 1:获取需要的数据,并且进行验证
            Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
            Compensate compensate = targetMethod.getAnnotation(Compensate.class);
            Object[] args = joinPoint.getArgs();
            if (args.length == 0) {
   
                logger.info("无参方法无法补偿!");
                return;
            }
            //异常类型
            Class<?>[] exceptionTypes = compensate.exceptionType();
            for (Class<?> exceptionType : exceptionTypes) {
   
                // exceptionType 是 e.getClass() 的父类或者一样的。
                if (exceptionType.isAssignableFrom(e.getClass())) {
   
                    saveCompensationDO(e, joinPoint);
                }
            }
        } catch (Throwable throwable) {
   
            // 异常需要抛出。要不然没办法,业务代码依赖这个报错的信息会被干扰。
            logger.error(throwable.getMessage(), throwable);
            Throwables.propagateIfPossible(throwable);
        }
    }

    /**
     * 2: 保存异常数据
     * 保存的数据,应该先查询下,看数据库中是否存在这样的数据,如果不存在就进行插入。存在啥也不做。
     *
     * @param e         异常类
     * @param joinPoint 切面类
     */
    public void saveCompensationDO(Exception e, JoinPoint joinPoint) throws JsonProcessingException {
   
        // 1:获取切面对象的参数
        MethodInfo methodInfo = new MethodInfo(joinPoint);
        methodInfo.init();
        // 2:构建需要补偿的对象
        CompensationEntity compensationDO = buildCompensationDO(methodInfo, e);
        // 3: 上面根据的数据,得保证是满足数据的完整性。然后进行数据的储存入库。然后结束。
        List<CompensationEntity> compensationEntities = compensationService.findByProperty(CompensationEntity.class, "busKey", compensationDO.getBusKey());
        if (CollectionUtils.isEmpty(compensationEntities)) {
   
            compensationService.save(compensationDO);
        }
    }

    /**
     * 2-1: 数据转换
     *
     * @param methodInfo 包装过后的数据对象
     * @return 转换的补偿类
     */
    private CompensationEntity buildCompensationDO(MethodInfo methodInfo, Exception e) {
   
        // 1:存入通用的参数
        CompensationEntity compensationDO = new CompensationEntity();
        Annotation[] annotations= methodInfo.getTargetClass().getAnnotations();
        String springBeanName="";
        for (Annotation annotation : annotations) {
   
            if (annotation.annotationType().equals(Service.class)){
   
                springBeanName=methodInfo.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值