美文网首页
通过自定义注解防止重复提交

通过自定义注解防止重复提交

作者: java_飞 | 来源:发表于2020-06-08 15:17 被阅读0次

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;
    }
}

相关文章

网友评论

      本文标题:通过自定义注解防止重复提交

      本文链接:https://www.haomeiwen.com/subject/rbtzzhtx.html