实现逻辑:
1、自定义注解
2、使用自定义注解修饰相关接口方法
3、通过aop扫描自定义注解修饰的接口,对相关接口进行统一幂等性处理
说明:
1、感觉类似乐观锁或者时令牌实现幂等性处理机制
2、有需要可在注解自行拓展允许过期时间赋值,毕竟不同的接口的需要的幂等性处理时间不一致
自定义注解
/**
* TODO: 用于修饰需要进行幂等性处理的接口,方便aop进行扫描并进行统一幂等性处理
* 该注解只用于方法上
*
* @author WKQ
* @date 2024/5/13
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
}
aop切面类
import com.common.utils.MyServiceException;
import com.order.common.session.CurrentTokenInfoUtil;
import net.jodah.expiringmap.ExpiringMap;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
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.util.concurrent.TimeUnit;
/**
* TODO: 幂等性处理aop,扫描自定义注解修饰的接口,使用对相关接口进行幂等性处理
*
* @author WKQ
* @date 2024/5/13
*/
@Aspect
@Component
public class AnnotationAop {
/**
* 存储 key -> url+用户id value -> 用户id,这里默认过期时间0.5秒
*/
ExpiringMap<String, Long> idempotentMap = ExpiringMap.builder()
.expiration(500, TimeUnit.MILLISECONDS)
.build();
/**
* 切入点为指定自定义幂等性注解
*/
@Pointcut("@annotation(Idempotent)")
public void log() {
}
/**
* 前置通知拦截
*/
@Before("log()")
public void deBefore(JoinPoint jp) throws Exception {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return;
}
// 获取当前用户id
Long userId = CurrentTokenInfoUtil.get().getUserId();
HttpServletRequest request = attributes.getRequest();
String key = request.getRequestURL().toString() + userId;
// 查看idempotentMap是否包含key,若包含请求不通过
if (idempotentMap.containsKey(key)) {
// 请求不通过
throw new MyServiceException("不要重复提交请求");
} else {
idempotentMap.put(key, userId);
}
}
}
使用注解修饰相关接口方法即可,aop内部缓存使用依赖,也可以用redis、ConcurrentHashMap等等方法实现相关功能
<dependency>
<groupId>net.jodah</groupId>
<artifactId>expiringmap</artifactId>
<version>0.5.8</version>
</dependency>