@Aspect @Component @Slf4j public class LimitAspect { private final RedisTemplate<Object, Object> redisTemplate;
public LimitAspect(RedisTemplate<Object, Object> redisTemplate) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); this.redisTemplate = redisTemplate; }
@Pointcut("@annotation(com.kangpan.annotation.Limit)") public void pointcut() { }
@Around("pointcut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); Method method = methodSignature.getMethod(); Limit limit = method.getAnnotation(Limit.class); String key = limit.key(); ImmutableList<Object> keys = ImmutableList.of(key); String lua = "local num" + "\n local key_local = redis.call('setnx',KEYS[1],0)" + "\n if (tonumber(key_local) == 1)" + "\n then" + "\n redis.call('incr',KEYS[1])" + "\n num = redis.call('get',KEYS[1])" + "\n if (tonumber(num) == 1)" + "\n then" + "\n redis.call('expire',KEYS[1],ARGV[2])" + "\n end" + "\n return tonumber(num);" + "\n else" + "\n redis.call('incr',KEYS[1])" + "\n num = redis.call('get',KEYS[1])" + "\n return tonumber(num);" + "\n end";
RedisScript<Number> redisScript = new DefaultRedisScript<>(lua, Number.class); Number number = redisTemplate.execute(redisScript, keys, limit.count(), limit.period()); if (null != number && number.intValue() <= limit.count()) { log.info("方法{}: 第{}次访问 有效时间为 {}", limit.key(), number, redisTemplate.getExpire(limit.key())); return proceedingJoinPoint.proceed(); } else { throw new BadRequestException("访问次数受限制"); } } }
|