一定时间内,同样的请求(业务参数相同)访问同一个接口,则只能成功一次,其余被拒绝。
import java.lang.annotation.*;
/**
* 幂等注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {
/**
* 幂等名称,作为redis缓存Key的一部分。
*/
String value();
/**
* 幂等过期时间,即:在此时间段内,对API进行幂等处理。
*/
long expireMillis();
}
```java
在这里插入代码片import cn.edu.nfu.jw.annotation.Idempotent;
import cn.edu.nfu.jw.exception.ServiceException;
import cn.edu.nfu.jw.util.KeyUtil;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
@ConditionalOnClass(RedisTemplate.class)
public class IdempotentAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(IdempotentAspect.class);
/**
* redis缓存key的模板
*/
private static final String KEY_TEMPLATE = "idempotent_%s";
@Resource
private RedisTemplate<String,String> redisTemplate;
/**
* 根据实际路径进行调整
*/
@Pointcut("@annotation(cn.edu.nfu.jw.annotation.Idempotent)")
public void executeIdempotent() {
}
@Around("executeIdempotent()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
ValueOperations<String, String> opsValue = redisTemplate.opsForValue();
//获取方法
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
//获取幂等注解
Idempotent idempotent = method.getAnnotation(Idempotent.class);
//根据 key前缀 + @Idempotent.value() + 方法签名 + 参数 构建缓存键值
//确保幂等处理的操作对象是:同样的 @Idempotent.value() + 方法签名 + 参数
//调用KeyUtil工具类生成key
String key = String.format(KEY_TEMPLATE, idempotent.value() + "_" + KeyUtil.generate(method.toString(), joinPoint.getArgs()));
//通过加锁确保只有一个接口能够正常访问
String s = opsValue.get(key);
if (s==null) {
//缓存请求的key
opsValue.set(key,"false",idempotent.expireMillis(),TimeUnit.MILLISECONDS);
return joinPoint.proceed();
} else {
LOGGER.info("当前时间:"+System.currentTimeMillis()+"");
throw new ServiceException(-1,"你的操作太频繁了!");
}
}
}```
//防止重复提交,value:请求方法名,expireMillis:重复提交频率,单位ms
@Idempotent(value = "/CssStudent/w-jxsqXkh", expireMillis = 3000L)
@RequestMapping("w-jxsqXkh")//@SessionAttribute("user")
public Object jxsqXkh(@RequestAttribute("user")BaseUser user,Long yjkcid,Long kczid,Long l3id,Long xyid,Integer kcxf,String xh,Integer kkxn,Integer xnxq,Double jxbxf,Long jxbid,Long dbid ,Boolean jxsqFlag,String reason,Long kkkcid,long pcid,Boolean fx, HttpServletRequest request, HttpServletResponse response) throws ServiceException {
Map<String,Object> map = new HashMap<>();
map.put("jxbid",jxbid);
map.put("dbid",dbid);
map.put("kkkcid",kkkcid);
map.put("pcid",pcid);
map.put("fx",fx);
map.put("reason",reason);
map.put("kkxn",kkxn);
map.put("xnxq",xnxq);
map.put("jxbxf",jxbxf);
map.put("l3id",l3id);
map.put("xyid",xyid);
map.put("kczid",kczid);
map.put("yjkcid",yjkcid);
return getSuccessJSON(studentService.jxsqXkh(map,user), request);
}