A lightweight single springboot solution to prevent duplicate submissions

A lightweight single springboot solution to prevent duplicate submissions

Material preparation

pom add dependency

<!-- 接口重复提交控制 -->
			<dependency>
				<groupId>net.jodah</groupId>
				<artifactId>expiringmap</artifactId>
				<version>0.5.10</version>
			</dependency>

Define a java annotation

package com.xxx.xxx.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidRepeatableCommit {
    
    

    /**
     * 指定时间内不可重复提交,单位毫秒
     */
    long timeout() default 3000;
}

Define the aspect corresponding to the annotation

package com.xxx.xxx.annotation;

import cn.hutool.core.util.StrUtil;
import com.xxx.xxx.exception.BusinessException;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class AvoidRepeatableCommitAspect {
    
    

    private static final long TIMEOUT = 3000L;

    private static final ExpiringMap<String, String> expiringMap = ExpiringMap.builder()
            .variableExpiration() // 可变有效期,即单独为每个entity设置过期时间和策略
            .expirationPolicy(ExpirationPolicy.CREATED) // 过期策略:CREATED:在每次更新元素时,过期倒计时清零
            .expiration(TIMEOUT, TimeUnit.MILLISECONDS) // 过期时间
            .maxSize(1000) // Map中映射数目超过最大值的大小时,先过期第一个要过期的entity
            .build();

    @Around("@annotation(com.xxx.xxx.annotation.AvoidRepeatableCommit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
    

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        Long account = (Long) WebUtils.getSessionAttribute(request, "cur_userId");
        if (account == null) {
    
    
            return point.proceed();
        }
        StringBuffer requestURL = request.getRequestURL();
        String reqMethod = request.getMethod();
        String key = String.format("%s_%s_%s", account, reqMethod, requestURL);

        // 获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        AvoidRepeatableCommit avoidRepeatableCommit = method.getAnnotation(AvoidRepeatableCommit.class);
        long timeout = avoidRepeatableCommit.timeout();
        if (timeout < 0) {
    
    
            timeout = TIMEOUT;
        }
        if (StrUtil.isNotBlank(expiringMap.get(key))) {
    
    
            throw new BusinessException("请勿重复提交");
        }
        expiringMap.put(key, UUID.randomUUID().toString(), ExpirationPolicy.CREATED, timeout, TimeUnit.MILLISECONDS);

        //执行方法
        return point.proceed();
    }

}

Use Cases

You can add this annotation to the method corresponding to @RequestMapping in your custom Controller class or RestController class.

@AvoidRepeatableCommit(timeout = 1000L)

In this way, the api interface marked by the annotation will be processed by the aspect when requesting, and it will be judged whether the front end has been submitted repeatedly.

Guess you like

Origin blog.csdn.net/ThinkPet/article/details/132788222
Recommended