避免重複提交解決方案之一

一、定義一個註解用於標註需要校驗重複提交的方法

package com.xwolf.boot.annotation;

import java.lang.annotation.*;

/**
 * 避免重複提交
 * @author xwolf
 * @version 1.0
 * @since 1.8
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidRepeatableCommit {

    /**
     * 指定時間內不可重複提交,單位毫秒
     * @return
     */
    long timeout()  default 30000 ;

}

二、添加一個切面用於校驗來自同一IP地址的重複提交請求

package com.xwolf.boot.aspect;

import com.xwolf.boot.annotation.AvoidRepeatableCommit;
import com.xwolf.boot.config.Constants;
import com.xwolf.boot.utils.IPUtil;
import com.xwolf.boot.utils.StringUtils;
import com.xwolf.boot.utils.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
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.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

/**
 * 重複提交aop
 * @author xwolf
 * @version 1.0
 * @since 1.8
 */
@Order
@Aspect
@Component
@Slf4j
public class AvoidRepeatableCommitAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @param point
     */
    @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request  = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = IPUtil.getIP(request);
        //獲取註解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //目標類、方法
        String className = method.getDeclaringClass().getName();
        String name = method.getName();
        String ipKey = String.format("%s#%s",className,name);
        int hashCode = Math.abs(ipKey.hashCode());
        String key = String.format("%s_%d",ip,hashCode);
        log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
        AvoidRepeatableCommit avoidRepeatableCommit =  method.getAnnotation(AvoidRepeatableCommit.class);
        long timeout = avoidRepeatableCommit.timeout();
        if (timeout < 0){
            timeout = Constants.AVOID_REPEATABLE_TIMEOUT;
        }
        String value = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)){
            return "請勿重複提交";
        }
        redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS);
        //執行方法
        Object object = point.proceed();
        return object;
    }

}


三、對需要檢驗重複提交的方法添加註解

 /**
     * 添加用戶
     * @param user
     * @return
     */
    @PostMapping(value = "add")
    @AvoidRepeatableCommit(timeout = 50000)
    public String insert(@Valid User user){
        // user.setBirth(new Date());
        log.info("請求參數:{}",user);
        return userService.insert(user);
    }


方案出自:https://github.com/fkandy/boot/tree/master/src/main/java/com/xwolf/boot

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章