实现原理
- 通过自定义注解标记哪些接口需要防范重复提交问题,并定义保持时间;
- 在Aspect中定义切点,织入所有被自定义注解标记的方法;
- 在Aspect中定义通知方法,通过PointCut获取类全名、被标记的方法名、参数名Json后的字符串,链接后MD5获取唯一签名,以签名为key,利用Redis的setNX实现重复校验。
自定义注解
package com.yxx.survey.foundation.aop;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmitCheck {
int keepSeconds() default 1;
}
Aspect
package com.yxx.survey.foundation.aop;
import com.alibaba.fastjson.JSON;
import com.yxx.survey.foundation.exception.ErrorEnum;
import com.yxx.survey.foundation.exception.excetion.BusinessException;
import com.yxx.survey.foundation.redis.RedisService;
import com.yxx.survey.foundation.util.encryption.HashUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {
@Resource
private RedisService redisService;
@Pointcut("@annotation(RepeatSubmitCheck)")
public void requestPointcut() {
}
@Before("requestPointcut() && @annotation(repeatSubmitCheck)")
public void aroundCheck(JoinPoint joinPoint, RepeatSubmitCheck repeatSubmitCheck) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
String paramsJsonStr = JSON.toJSONString(args);
String srcStr = className + "_" + methodName + "_" + paramsJsonStr;
log.info(srcStr);
String signStr = HashUtil.MD5(srcStr);
if (!redisService.setIfNotExists(signStr, "1", repeatSubmitCheck.keepSeconds())) {
throw BusinessException.with(ErrorEnum.INTERFACE_INVOKE_FREQUENTLY);
}
}
}
应用
@PostMapping("/public/verificationCode")
@RepeatSubmitCheck(keepSeconds = 30)
public ResponseEntity<Map<String, String>> sendVerificationCode(@Valid @RequestBody LoadVerifyCodeReq req) {
}
网友评论