1. 自定义注解
package xxxxxx;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author David
* @Description: 防止重复提交的注解
* @date 2020/6/8 10:38 上午
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatCommit {
/**
* 超时时间,默认1s,毫秒为单位;因为程序执行完以后有删除key的操作,所以这里的过期时间设置可以稍微长点
*/
long expireTime() default 1000;
/**
*
* spEL表达式,主要是为了某些接口需要自定义的key实现
* 正常参数 获取示例:#id 对象参数:示例 #user.id
*/
String value() default "";
}
2.aop拦截方法实现
package xxxxxx;
import com.mmhsy.dict.cache.CacheConstant;
import com.mmhsy.exception.BusinessException;
import com.mmhsy.repeat.annotation.RepeatCommit;
import com.mmhsy.util.CacheUtil;
import com.mmhsy.util.MD5Util;
import com.mmhsy.util.RedisTemplateUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* @author David
* @Description: 防止重复提交的aop类
* @date 2020/6/8 11:01 上午
*/
@Slf4j
@Component
@Aspect
public class RepeatCommitAop {
private SpelExpressionParser spElParser = new SpelExpressionParser();
@Around("@annotation(com.mmhsy.repeat.annotation.RepeatCommit)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
List<String> paraNameList = Arrays.asList(signature.getParameterNames());
List<Object> paraValueList = Arrays.asList(pjp.getArgs());
RepeatCommit repeatCommit = method.getAnnotation(RepeatCommit.class);
String methodName = method.getName();
//这个地方是获取系统内的用户登陆id,视自己的系统而定
Long loginUserId = xxxxxxxxxx;
//未登录的接口直接调用不需要做限制
if(Objects.isNull(loginUserId)){
return pjp.proceed();
}
String value = parseSpEL(paraNameList, paraValueList, repeatCommit.value());
String redisKey = getRedisKey(loginUserId, methodName, value);
boolean b1 = redis.setNx(redisKey, 0, repeatCommit.expireTime());
//设置失败说明缓存中存在该值
if (!b1) {
throw new BusinessException("请勿频繁操作");
}
Object proceed = pjp.proceed();
//执行完,删除该key
redis.delete(redisKey);
return proceed;
}
/**
* 解析spEL表达式
* @param paraNames
* @param paraValues
* @param spEl
* @return
*/
private String parseSpEL(List<String> paraNames, List<Object> paraValues, String spEl) {
//将方法的参数名和参数值一一对应的放入上下文中
EvaluationContext ctx = new StandardEvaluationContext();
for (int i = 0; i < paraNames.size(); i++) {
ctx.setVariable(paraNames.get(i), paraValues.get(i));
}
return Objects.requireNonNull(spElParser.parseExpression(spEl).getValue(ctx)).toString();
}
/**
* 获取redisKey
*
* @param userId
* @param methodName
* @param expandStr
* @return
*/
private String getRedisKey(@NonNull Long userId, @NonNull String methodName, String expandStr) {
StringBuilder beforeMd5str = new StringBuilder();
beforeMd5str.append(userId).append(methodName);
if (StringUtils.isNotBlank(expandStr)) {
beforeMd5str.append(expandStr);
}
String md5String = MD5Util.getMD5String(beforeMd5str.toString());
return CacheConstant.REPEAT_CLIENT_KEY + md5String;
}
}
网友评论