spring Aop实现防止重复提交

Posted 全力以赴001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring Aop实现防止重复提交相关的知识,希望对你有一定的参考价值。

1.先定义一个注解

import java.lang.annotation.*;

/**
 * @desc 定义一个不重复提交的注解
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatCommit {

    String name() default "name:";
}

 

2.实现一个aop

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * @desc 防止重复提交aop
 */
@EnableAspectJAutoProxy
@Component
@Aspect
@Slf4j
@Configuration
public class NoRepeatSubmitAspect {

    private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
            // 最大缓存 100 个
            .maximumSize(100)
            // 设置缓存过期时间为S
            .expireAfterWrite(2, TimeUnit.SECONDS)
            .build();

    @Pointcut("@annotation(com.shandie.im.web.aop.NoRepeatCommit)")
    public void point() {
    }

    @Around("point()")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        NoRepeatCommit form = method.getAnnotation(NoRepeatCommit.class);
        String key = getCacheKey(method,pjp,form);

        if (!StringUtils.isEmpty(key)) {
            if (CACHES.getIfPresent(key) != null) {
                return RestResultDto.fail("请勿重复请求");
            }
            // 如果是第一次请求,就将key存入缓存中
            CACHES.put(key, key);
        }
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException("服务器开小差了,请稍后再试");
        } finally {
            CACHES.invalidate(key);
        }
    }

    /**
     * 缓存key生成方式
     * @param method
     * @param pjp
     * @param form
     * @return
     */
    private String getCacheKey(Method method, ProceedingJoinPoint pjp, NoRepeatCommit form) {
        try{
            Map<String, Object> map=getFieldsNameValueMap(pjp);
            if(ObjectHelper.isNotEmpty(map) && map.size()>0){
                HpBaseDto baseDto= JSON.parseObject(JSON.toJSONString(map),HpBaseDto.class);
                if(ObjectHelper.isNotEmpty(baseDto) && ObjectHelper.isNotEmpty(baseDto.getUserId())){
                    return form.name()+baseDto.getUserId();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return form.name()+ CustomIDGenerator.generate();
    }

    public static Map<String, Object> getFieldsNameValueMap(ProceedingJoinPoint joinPoint) throws Exception {
        Object[] args = joinPoint.getArgs();
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        Map<String, Object> map = Maps.newHashMap();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(NoRepeatCommit.class);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            throw new RuntimeException();
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        //paramNames即参数名
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            map.put(attr.variableName(i + pos), args[i]);
        }
        return map;
    }
}

 

3.使用

 @PostMapping("/test")
    @NoRepeatCommit
    public RestResultDto aa(){
        return RestResultDto.success("success");
    }

 

4.使用的包

 

以上是关于spring Aop实现防止重复提交的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud项目如何防止重复提交,防重复提交幂等校验,Redis+aop+自定义Annotation实现接口

Spring Boot 框架中如何使用 AOP 防止重复提交?

Springboot 使用AOP实现防止接口重复提交

Aop+Redis防止接口重复提交

spring boot 通过AOP防止API重复请求

Struts2 06--系统拦截器防止数据重复提交