防重复操作的一种实现

分享一种防重复操作的实现方式,基于注解和redis锁的实现方式。

重复请求枚举:
/**
 * 重复请求枚举
 * TOKEN 根据token强制重复
 * FINAL 指定key
 * SP_EL 根据入参
 * @author zhangtaotao
 * @since 2020/11/16 16:50
 **/
public enum NonRepeatEnum {
    TOKEN,
    FINAL,
    SP_EL;
}
注解的定义:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NonRepeat {

    /**
     * 锁定时间 seconds
     *
     * @return
     */
    int lockTime() default 120;


    /**
     * lockKey策略
     *
     * @see NonRepeatEnum
     */
    NonRepeatEnum lockKeyPolicy() default NonRepeatEnum.TOKEN;

    /**
     * 前缀 psi:common:repeat:{className}:{methodName}:
     * 默认根据 token
     * 支持 spEL 取值非string类型会序列化为json,JSON.toJsonString
     *
     * @return
     */
    String lockKey() default "";

    /**
     * 是否在执行结束后释放锁
     *
     * @return
     */
    boolean releaseAfterReturn() default true;
}
重复请求控制切面逻辑:
/**
 * 重复请求限制
 *
 * @author zhangtaotao
 * @since 2020/11/16 17:24
 **/
@Slf4j
@Aspect
@Component
public class NonRepeatAspect {

    private GlobalLockAppService globalLockAppService;

    private final ExpressionParser parser = new SpelExpressionParser();

    @Around(value = "@annotation(nonRepeat)")
    public Object before(ProceedingJoinPoint pjp, NonRepeat nonRepeat) throws Throwable {
        UserLocal.USER.saveLocal(Locale.CHINESE);
        Object[] args = pjp.getArgs();
        String lockKey = buildLockKey(args, nonRepeat);
        String className = pjp.getTarget().getClass().getName();
        String methodName = pjp.getSignature().getName();
        String realKey =
                String.join(BaseConstants.COLON, RedisConstants.REDIS_REPEAT_LOCK, className, methodName, lockKey);
        GlobalLock globalLock = GlobalLock.of(realKey, Duration.ofSeconds(nonRepeat.lockTime()));
        if (!globalLockAppService.getLock(globalLock)) {
            throw new BizsException(ResultEnum.NON_REPEAT_ERROR);
        }
        Object result;
        try {
            result = pjp.proceed();
        } finally {
            if (nonRepeat.releaseAfterReturn()) {
                globalLockAppService.unlock(globalLock);
            }
        }
        return result;
    }

    private String buildLockKey(Object[] args, NonRepeat nonRepeat) {
        String lockKey = null;
        NonRepeatEnum lockKeyPolicy = nonRepeat.lockKeyPolicy();
        Objects.requireNonNull(lockKeyPolicy, "can not find lock key policy");
        switch (lockKeyPolicy) {
            case FINAL:
                lockKey = nonRepeat.lockKey();
                break;
            case SP_EL:
                lockKey = buildKeyForExpression(args, nonRepeat);
                break;
            case TOKEN:
                lockKey = UserLocal.USER.getToken();
                break;
            default:
                break;
        }
        return lockKey;
    }

    private String buildKeyForExpression(Object[] args, NonRepeat nonRepeat) {
        Objects.requireNonNull(args, "args can not be null");
        String lockKey;
        if (StringUtils.isEmpty(nonRepeat.lockKey())) {
            throw new BizsException(ResultEnum.VALIDATE_PARAM_ERROR);
        }
        Expression exp = parser.parseExpression(nonRepeat.lockKey());
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        if (args.length == 1) {
            ctx.setRootObject(args[0]);
        } else {
            ctx.setRootObject(args);
        }
        Object value = exp.getValue(ctx);
        if (value instanceof String) {
            lockKey = String.valueOf(value);
        } else {
            lockKey = JSON.toJSONString(value);
        }
        return lockKey;
    }

    @Autowired
    public void setGlobalLockAppService(GlobalLockAppService globalLockAppService) {
        this.globalLockAppService = globalLockAppService;
    }
}
应用

getLockKey()方法定义在入参DoApproveFlowVO中,用于定义锁的唯一内容

	@NonRepeat(lockKeyPolicy = NonRepeatEnum.SP_EL, lockKey = "getLockKey()")
    @ApiOperation(value = "审批操作接口")
    @PostMapping(value = "/doApprove")
    public ResponseResult<Object> doApprove(@RequestBody @Validated DoApproveFlowVO doApproveFlowVO) {
    	//审批操作
    }

至于redis锁的实现可以参考另外一篇文章:redis锁的一种实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值