使用切面实现前端重复提交(防抖)

文章介绍了如何使用SpringAOP和Redis实现前端提交操作的防抖功能,通过注解指定关键字段,避免重复提交并提供相应的缓存和防抖处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用切面实现前端重复提交(防抖)

代码结构

在这里插入图片描述

原理:
1、前端提交保存操作;
2、后端通过注解指定重复提交的关键字段进行识别,可以有多个;
3、拼接关键字段,缓存到redis中,设置到期时间(默认3秒);
4、命中缓存则进行防抖处理,否则进行正常业务。

定义注解

/**
 * @description 请求防抖锁,用于防止前端重复提交导致的错误
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestLock {
	/**redis 锁前缀 */
	String prefix() default "";
	/**redis 锁过期时间, 默认3秒 */
	int expire() default 3;
	/** redis 锁过期时间单位,默认单位为秒 */
	TimeUnit timeUnit() default TimeUnit.SECONDS;
	/** redis key分隔符, 分隔符 */
	String delimiter() default ":";
	/** 防抖关键标识,使用spring el 表达式<br>
	 * 例子:<br>
	 * 第一个参数(对象)中的一个属性(电话号码) : params[0].phone<br>
	 * 第一个参数(Map)中的键值对(电话号码) : params[0]['phone']<br>
	 */
	String[] keys();
}

请求锁切面处理器

/**
 * @description 请求锁切面处理器
 */
@Aspect
@Configuration
public class RequestLockAspect {
	@Autowired
	StringRedisTemplate stringRedisTemplate;

	/** 切入点声明 */
	@Pointcut("execution(public * * (..)) && @annotation(zzc.learn.springboot.demo.repeatsubmit.annotation.RequestLock)")
	public void pointcut() {
		// do nothing
	}

	/** 定义切面:环绕通知  */
	@Around("pointcut()")
	public Object interceptor(ProceedingJoinPoint joinPoint) {
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		Method method = methodSignature.getMethod();
		RequestLock requestLock = method.getAnnotation(RequestLock.class);
		if (StringUtils.isEmpty(requestLock.prefix())) {
			//            throw new RuntimeException("重复提交前缀不能为空");
			return "重复提交前缀不能为空";
		}
		//获取自定义key
		final String lockKey = _getLockKey(joinPoint);
		final Boolean success = stringRedisTemplate.execute(
				(RedisCallback<Boolean>) connection -> connection.set(lockKey.getBytes(), new byte[0], Expiration.from(requestLock.expire(), requestLock.timeUnit())
						, RedisStringCommands.SetOption.SET_IF_ABSENT));
		if (!success) {
			//            throw new RuntimeException("您的操作太快了,请稍后重试");
			return "您的操作太快了,请稍后重试";
		}
		try {
			return joinPoint.proceed();
		} catch (Throwable throwable) {
			//            throw new RuntimeException("系统异常");
			return "系统异常";
		}
	}

	/** 获取防抖提交对应的rediss的key */
	private String _getLockKey(ProceedingJoinPoint joinPoint) {
		//获取连接点的方法签名对象
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		//Method对象
		Method method = methodSignature.getMethod();
		//获取Method对象上的注解对象
		RequestLock requestLock = method.getAnnotation(RequestLock.class);
		//获取方法参数
		List<Object> spelValueist = _getSpelValueList(requestLock, joinPoint.getArgs());

		StringBuilder sb = new StringBuilder();
		for (Object object : spelValueist) {
			sb.append(requestLock.delimiter()).append(object);
		}
		//返回指定前缀的key
		return requestLock.prefix() + sb;
	}

	/** 根据spel表达式读取对应参数的值 */
	private List<Object> _getSpelValueList(RequestLock requestLock, final Object[] args) {
		List<Object> spelValueist = new ArrayList<>();
		ExpressionParser parser = new SpelExpressionParser();
		EvaluationContext context = new StandardEvaluationContext();
		context.setVariable("params", args);
		String[] spelArr = requestLock.keys();
		for (int i = 0; i < spelArr.length; i++) {
			String expressionString = spelArr[i];
			if(StringUtils.isBlank(expressionString)) {
				continue;
			}
			Expression expression = parser.parseExpression(expressionString);
			spelValueist.add(expression.getValue(context));
		}
		return spelValueist;
	}
}

入参对象

@Data
public class User {
	private String name;
	private Integer age;
	private String phone;
}

使用注解

@Api(value = "重复提交测试", tags = {"重复提交测试"})
@RestController
@RequestMapping("/repeatsubmit/user")
public class UserController {

	@ApiOperation(value = "增加用户(不限制重复提交)", notes = "增加用户1")
	@PostMapping("/addUser1")
	public String addUser1(@RequestBody User user) {
		System.out.println("不做任何处理" + user);
		return "添加成功";
	}

	@ApiOperation(value = "增加对象类型用户(限制重复提交)", notes = "增加用户2")
	@PostMapping("/addUser2")
	@RequestLock(prefix = "addUser", keys= {"#params[0].name","#params[0].phone"})
	public String addUser2(@RequestBody User user, @RequestParam String name) {
		System.out.println("防重提交" + user);
		return "添加成功";
	}

	@ApiOperation(value = "增加Map类型用户(限制重复提交)", notes = "增加用户3")
	@PostMapping("/addUser3")
	@RequestLock(prefix = "addUser", keys = {"#params[0]['phone']"})
	public String addUser3(@RequestBody Map<String, Object> paramMap, @RequestParam String name) {
		System.out.println("防重提交" + paramMap);
		return "添加成功";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_26264237

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值