使用AOP切面做防止用户重复提交功能

         在我们的项目中,需要考虑到有时候因为网络原因或者其他原因用户对同一个接口进行同一批数据的重复性操作,如果不做这样的处理很可能会在数据库中添加多条同样的数据。

我们可以通过使用aop来解决这样的问题,接下来看看具体怎么做吧~

自定义防重复提交注解

关于如何做自定义注解,请看以下博客

springboot做自定义校验注解

这篇博客我们先跳过,直接来看实现

package com.yinan.ParameterValidation.paramInterface;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
// 缓存时间默认为10秒,请求完成自动清除,过期自动清除
    long cacheTime() default 10000;
}

接下来使我们的关键代码,aop功能的实现

package com.yinan.aop;


import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import com.yinan.ParameterValidation.paramInterface.RepeatSubmit;
import com.yinan.constant.BusinessResponseStatus;
import com.yinan.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {
    /**
     * 缓存有效时间为10秒
     */
    private static final TimedCache<String, Integer> cache = CacheUtil.newTimedCache(10000);

    @Around("@annotation(com.yinan.ParameterValidation.paramInterface.RepeatSubmit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 是否重复请求异常
        Boolean isException10002 = false;
        try{
//            获取请求数据作为key:sessionId+Path
            ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String sessionId=attributes.getSessionId();
            HttpServletRequest request=attributes.getRequest();
            String path=request.getServletPath();
            String key=sessionId+"-"+path;

//            获取@RepeatSubmit注解
            MethodSignature signature=(MethodSignature) point.getSignature();
            Method method=signature.getMethod();
            RepeatSubmit repeatSubmit=method.getAnnotation(RepeatSubmit.class);

//            如果缓存中有这个URl视为重复提交
            if(cache.get(key)==null){
//                缓存
                cache.put(key,0,repeatSubmit.cacheTime());
//                请求
                Object result=point.proceed();
//                请求成功,移除缓存
                cache.remove(key);
                return result;
            }else{
                isException10002=true;
            }
        }catch (Exception e){
            log.error("重复提交异常,{}",e.getMessage());
        }
        if(!isException10002){
            throw new BusinessException("验证重复提交时出现未知异常", BusinessResponseStatus.HTTP_STATUS_10002.getResponseCode());
        }
        throw new BusinessException(BusinessResponseStatus.HTTP_STATUS_10002.getDescription(), BusinessResponseStatus.HTTP_STATUS_10002.getResponseCode());
    }
}

剩下的就是异常的处理了

package com.yinan.constant;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum BusinessResponseStatus {

    SUCCESS("200", "success"),
    FAIL("500", "服务器异常"),
    HTTP_STATUS_200("200", "success"),
    HTTP_STATUS_400("400", "请求异常"),
    HTTP_STATUS_401("401", "no authentication"),
    HTTP_STATUS_403("403", "no authorities"),
    HTTP_STATUS_500("500", "server error"),
    HTTP_STATUS_10001("10001", "系统繁忙"),
    HTTP_STATUS_10002("10002", "重复请求,请稍后再试试");
    /**
     * 响应码
     */
    private final String responseCode;

    /**
     * 响应描述
     */
    private final String description;

}
package com.yinan.exception;

import com.yinan.constant.BusinessResponseStatus;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BusinessException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    /**
     * 响应码
     */
    private String status = BusinessResponseStatus.HTTP_STATUS_400.getResponseCode();

    /**
     * 结果信息
     */
    private String message = BusinessResponseStatus.HTTP_STATUS_400.getDescription();

    public BusinessException(String message) {
        this.message = message;
    }

    public BusinessException(String message,String status) {
        this.message = message;
        this.status = status;
    }

}

controller层的处理

以上功能实现后,我们只需要在controller层的接口上添加@RepeatSubmit注解就行了

使用apifox进行验证

 

以上是我已经进行新增一次再次新增时的提示内容,说明重复提交功能成功实现~ 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

银氨溶液

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值