什么是幂等?
用户对于同一操作发起的一次请求或者多次请求的结果是一致的。
数据库操作中:SELECT UPDATE DELETE 操作天然就是幂等的,同样的语句执行多次结果都不会产生变化,唯一的就是受影响的行数会变化,但 INSERT 插入操作则不是(在未指定主键或唯一性字段的前提下);所以需要我们在Java层面保证请求为幂等。否则会出现多次下单、数据异常、扣款重复等情况。闲话少说,说时迟那时快,抄起键盘就是干!
1、定义一个幂等校验的注解,使用的时候放在需要保证幂等的请求方法上即可。
/**
* 标识接口需要保证幂等,未登录接口请勿使用
* @author Jiang Jun
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
int interval() default 5000;
/**
* 提示消息
*/
String message() default "不允许重复提交,请稍候再试";
}
2、定义一个拦截器,在 preHandle 方法中做幂等性校验
其中一些我系统本身自定义的类可以替换成你自己工程的类,主要的校验逻辑不受影响,校验是否幂等我采用的判断方式是:使用 Redis 的 String 类型存储请求参数,用户 ID+URI 作为 Key 保证接口请求的唯一性,Value 存储的是本次请求参数的 MD5摘要,MD5担心有的小伙伴不懂我解释一下: MD5 即 Message-Digest Algorithm 5(信息-摘要算法5)常用于文件校验。不管文件多大,经过 MD5 后都能生成唯一的 MD5 值。
/**
* 对方法上标注了幂等请求注解进行幂等校验
* @author Jiang Jun
*/
@Component
public class IdempotentInterceptor implements HandlerInterceptor {
@Resource
private RedissonClient redissonClient;
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Idempotent annotation = method.getAnnotation(Idempotent.class);
if (annotation != null) {
// 判断是否为重复提交
if (this.isRepeatSubmit(request, annotation)) {
String responseBody = JSON.toJSONString(ResponseResult.error(ErrorCodeEnum.NO_ERROR, annotation.message()));
response.setStatus(