spring防止重复点击,两种注解实现(AOP)
【摘要】 在Spring应用中,防止重复点击是一个常见的需求。常见的实现方式包括使用注解与AOP(面向切面编程)结合的方法。以下内容将对两种基于注解的实现方式进行介绍,并包含详细的代码示例、测试方案、以及材料链接等。 一、概述 应用场景防止重复点击主要适用于以下几种情况:短时间内高频提交表单。防止用户误操作导致的重复请求。保护关键业务接口的幂等性。 原理解释通过AOP切面监控目标方法,拦截请求并检测快...
在Spring应用中,防止重复点击是一个常见的需求。常见的实现方式包括使用注解与AOP(面向切面编程)结合的方法。以下内容将对两种基于注解的实现方式进行介绍,并包含详细的代码示例、测试方案、以及材料链接等。
一、概述
应用场景
防止重复点击主要适用于以下几种情况:
- 短时间内高频提交表单。
- 防止用户误操作导致的重复请求。
- 保护关键业务接口的幂等性。
原理解释
通过AOP切面监控目标方法,拦截请求并检测快速重复的请求,根据判定条件来决定是否放行或抛出异常。通常通过缓存(如Redis)保存每个请求的信息和时间戳,通过这些信息识别重复请求。
二、算法原理流程图
+---------------------------+
| User Sends Request |
+---------------------------+
|
v
+---------------------------+
| Check for Cached Token |
+---------------------------+
/ \
/ \
Yes No
/ \
v v
+--------+ +-------------------+
| Reject | | Cache the Token |
| Req. | | and Proceed with |
+--------+ | Business Logic |
+-------------------+
三、实现方式
实现方式 1:自定义注解 + AOP
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoRepeatSubmit {
int timeout() default 5; // 默认5秒
}
AOP 切面
@Aspect
@Component
public class NoRepeatSubmitAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(noRepeatSubmit)")
public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String key = request.getSession().getId() + "-" + request.getServletPath();
if (redisTemplate.hasKey(key)) {
throw new RuntimeException("Please do not repeat submit.");
}
redisTemplate.opsForValue().set(key, "LOCK", noRepeatSubmit.timeout(), TimeUnit.SECONDS);
try {
return joinPoint.proceed();
} finally {
redisTemplate.delete(key);
}
}
}
应用到Controller
@RestController
public class ExampleController {
@NoRepeatSubmit(timeout = 10)
@PostMapping("/submit")
public ResponseEntity<String> submitData() {
// Business logic
return ResponseEntity.ok("Success");
}
}
实现方式 2:注解 + Guava Cache
Guava Cache 版本
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoRepeatSubmit {
int timeout() default 5;
}
@Aspect
@Component
public class NoRepeatSubmitAspect {
private final LoadingCache<String, Boolean> cache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build(new CacheLoader<String, Boolean>() {
public Boolean load(String key) {
return false;
}
});
@Around("@annotation(noRepeatSubmit)")
public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String key = request.getSession().getId() + "-" + request.getServletPath();
if (cache.getUnchecked(key)) {
throw new RuntimeException("Please do not repeat submit.");
}
cache.put(key, true);
try {
return joinPoint.proceed();
} finally {
cache.invalidate(key);
}
}
}
四、测试代码
@SpringBootTest
public class NoRepeatSubmitTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testNoRepeatSubmit() throws Exception {
mockMvc.perform(post("/submit")).andExpect(status().isOk());
mockMvc.perform(post("/submit")).andExpect(status().is4xxClientError());
}
}
五、部署场景
部署时需要确保应用环境中有Redis可用(对于Redis实现)。
六、材料链接
以下是一些供进一步学习的链接:
七、总结
通过自定义注解结合AOP的方式,我们能够有效地拦截和防止短时间内的重复请求,为系统的幂等性提供了保障。这种方法也展示了Spring框架的强大灵活性。
八、未来展望
随着微服务架构和无服务器架构的发展,防止重复请求的策略也需要不断演变。未来可能会更多依赖分布式锁或者其他轻量级的消息队列来处理复杂的业务场景。同时,AI技术的进步可能带来更智能的流量分析和预测工具,可以更好地防范恶意或意外的重复请求。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)