AOP + Redis实现防止表单重复提交(注解方式)

  • 引入SpringAOP
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 配置redis,并创建redis工具类

               redis配置此处略过

@Service
@Slf4j
public class RedisService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public boolean existKey(String key) {
        log.debug("existKey().params:" + key);
        return stringRedisTemplate.hasKey(key);
    }

    public Boolean delKey(String key) {
        log.debug("delKey().params:" + key);
        Boolean delete = stringRedisTemplate.delete(key);
        return delete;
    }

    public boolean addString(String key, String value) {
        BoundValueOperations<String, String> ops = stringRedisTemplate.boundValueOps(key);
        try {
            ops.set(value);
            return true;
        } catch (Exception e) {
            log.error("redis添加异常." + e.getMessage(), e);
            e.printStackTrace();
            return false;
        }
    }

    public boolean addString(String key, String value, long timeout, TimeUnit timeUnit) {

        BoundValueOperations<String, String> ops = stringRedisTemplate.boundValueOps(key);
        try {
            ops.set(value, timeout, timeUnit);
            return true;
        } catch (Exception e) {
            log.error("redis添加异常." + e.getMessage(), e);
            e.printStackTrace();
            return false;
        }
    }
}
  • 创建注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FormLock {

    /**
     * 指定时间内不可重复提交,单位毫秒
     * @return
     */
    long timeout() default 3000;

}
  • 创建AOP环绕通知处理类
@Slf4j
@Aspect
@Component
public class FormLockAspect {

    @Autowired
    private RedisService redisService;

    @Pointcut("@annotation(com.wetran.codex.business.avoid_repetition.AFormLock)")
    public void reqLock() {
    }

    /**
     * @param point
     */
    @Around("reqLock()")
    public Object constructionSite(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        //获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //目标类、方法
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        //由于使用方法名+用户ID创建的Key,,,必须要有用户id参数
        Long userId = Long.parseLong(request.getParameter("id"));
        log.info("防止表单重复提交类:{}. 方法:{}.用户ID:{}.", className, methodName, userId);
        String key = RedisConstants.BLACKlIST_LOCK_KEY + methodName + ":" + userId;

        FormLock formLock = method.getAnnotation(FormLock.class);
        long timeout = formLock.timeout();
        if (timeout < 0) {
            //过期时间3000毫秒
            timeout = 3000;
        }
        //校验该方法该用户的key是否存在,存在直接返回错误
        if (redisService.existKey(key)) {
            return RespUtils.fail("请勿重复提交");
        }
        boolean b = redisService.addString(key, DateUtils.getNow(), timeout, TimeUnit.MILLISECONDS);
        log.info("创建锁结果:{}. key:{}.", b, key);
        //执行方法
        Object object = point.proceed();

        //整理施工现场
        Boolean aBoolean = redisService.delKey(key);
        log.info("删除redis锁结果:{}.", aBoolean);
        return object;
    }
}

redis有效时间根据自身业务需求进行调整即可

在需要防止表单重复提交的方法上添加@FormLock注解即可,可根据不同方法指定相应的有效时间

猜你喜欢

转载自blog.csdn.net/qq_42407917/article/details/100151015