在我们的项目中,需要考虑到有时候因为网络原因或者其他原因用户对同一个接口进行同一批数据的重复性操作,如果不做这样的处理很可能会在数据库中添加多条同样的数据。
我们可以通过使用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进行验证
以上是我已经进行新增一次再次新增时的提示内容,说明重复提交功能成功实现~