自定义接口防抖/防止重复提交的注解

1.定义一个切面

@Aspect
@Component
public class NoRepeatSubmitAspect {
}


2.定义切点

@Aspect
@Component
public class NoRepeatSubmitAspect {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;

    //切点是所有注解的方法
    @Pointcut("@annotation(com.bairh.cs.provider.controller.busi.NoRepeat)")
    public void pointCut(){};

}

3.定义增强

@Aspect
@Component
public class NoRepeatSubmitAspect {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;

    @Pointcut("@annotation(com.bairh.cs.provider.controller.busi.NoRepeat)")
    public void pointCut(){};

    @Before("pointCut()")
    public void before(JoinPoint joinPoint) throws IOException {
        //获取corTicket
        String corTicket = request.getHeader("Corticket");
        MethodSignature signature =(MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();

        //获取方法名
        String methodName = method.getName();
        //获取类名
        Class<?> declaringClass = method.getDeclaringClass();
        String className = declaringClass.getName();
        //获取注解的timeOut值
        NoRepeat annotation = method.getAnnotation(NoRepeat.class);
        long timeOut = annotation.timeOut();
        System.out.println(corTicket+"-"+className+"-"+methodName);

        if (!redisTemplate.hasKey(corTicket + "-" + className + "-" + methodName)){
            redisTemplate.opsForValue().set(corTicket+"-"+className+"-"+methodName,"true",timeOut, TimeUnit.SECONDS);
        }
        else {
            throw  new RuntimeException("不可重复提交");
        }
    }
}

4.定义注解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeat {
    long timeOut() default 5L;
}


5.使用注解

    @NoRepeat
    @PostMapping("/save")
    public Object save(){
           ...
    }

### Java 中通过自定义注解实现防抖功能 #### 自定义注解定义 为了实现防抖功能,可以创建一个名为 `@AntiShake` 的自定义注解。此注解允许开发者指定方法调用之间的最小间隔时间(默认为1秒)。以下是其具体实现: ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 注解放置在方法上 @Target(ElementType.METHOD) // 运行时保留以便于动态代理或切面编程使用 @Retention(RetentionPolicy.RUNTIME) public @interface AntiShake { // 前缀键,默认为空字符串 String preKey() default ""; // 防抖时间,默认为1000毫秒 long value() default 1000L; // 时间单位,默认为毫秒 java.util.concurrent.TimeUnit timeUnit() default java.util.concurrent.TimeUnit.MILLISECONDS; } ``` 以上代码片段展示了如何定义一个带有属性的自定义注解[^2]。 #### 利用 AOP 实现防抖逻辑 Spring AOP 提供了一种优雅的方式来拦截标注有特定注解的方法并附加额外的行为。下面是一个基于 Spring AOP 的示例,展示如何利用 Redis 或内存缓存来实现防抖效果。 ##### 编写切面类 ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Aspect @Component public class AntiShakeAspect { @Resource private StringRedisTemplate redisTemplate; @Around("@annotation(antiShake)") public Object aroundAdvice(ProceedingJoinPoint joinPoint, AntiShake antiShake) throws Throwable { StringBuilder keyBuilder = new StringBuilder(); // 构造唯一的key值 if (!"".equals(antiShake.preKey())) { keyBuilder.append(antiShake.preKey()).append(":"); } keyBuilder.append(joinPoint.getSignature().toShortString()); String key = keyBuilder.toString(); Boolean lockSuccess = tryLock(key, antiShake.value(), antiShake.timeUnit()); if (lockSuccess != null && lockSuccess) { try { return joinPoint.proceed(); } finally { releaseLockAfterTimeout(key); } } throw new RuntimeException("请求过于频繁,请稍后再试!"); } /** * 尝试获取锁 */ private Boolean tryLock(String key, long duration, TimeUnit unit) { Boolean success = redisTemplate.opsForValue() .setIfAbsent(key, "locked", unit.toSeconds(duration)); return success; } /** * 超时释放锁 */ private void releaseLockAfterTimeout(String key) { redisTemplate.expire(key, 1, TimeUnit.SECONDS); // 设置短暂过期时间以清理资源 } } ``` 上述代码实现了对带 `@AntiShake` 注解方法的环绕通知,并借助 Redis 来管理分布式环境下的防抖状态[^5]。 #### 测试案例 假设存在如下服务类及其方法: ```java @Service public class DemoService { @AntiShake(value = 3000, preKey = "demo") public String doSomething() { return "操作成功"; } } ``` 当多次快速调用 `doSomething()` 方法时,只有第一次会正常执行;后续调用会在设定的时间窗口内被拒绝,直到计时期满为止[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值