定义接口访问频率注解
/** * 接口访问频率注解,默认一分钟只能访问60次 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit { // 限制时间 单位:秒(默认值:一分钟) long period() default 60; // 允许请求的次数(默认值:60次) long count() default 60; }
定义切点和处理逻辑,这里使用了redis进行计数
@Aspect
@Component
@Log4j2
public class RequestLimitAspect {@Autowiredprivate RedisUtil redisUtil;// 切点@Pointcut("@annotation(requestLimit)")public void controllerAspect(RequestLimit requestLimit) {}@Around("controllerAspect(requestLimit)")public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {// get parameter from annotationlong period = requestLimit.period();long limitCount = requestLimit.count();String key = "requestLimit";Object countStr = redisUtil.get(key);if (countStr == null) {redisUtil.set(key, "1",period);return joinPoint.proceed();} else {int count = Integer.parseInt(String.valueOf(countStr));if (count >= limitCount) {log.error("接口拦截:请求超过限制频率");return HyResponse.fail("超过请求次数,请一分钟后再次请求!");}else {redisUtil.set(key, String.valueOf(count + 1), period);return joinPoint.proceed();}}}
}
最后直接在需要限制的请求接口上加上@RequestLimit注解
@RequestLimit(count = 60)@GetMapping("/test")public HyResponse test() {return HyResponse.success();}
RedisUtil工具类
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;public class RedisUtil {private RedisTemplate<String, Object> redisTemplate;public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}// =============================common============================/*** 指定缓存失效时间** @param key 键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}}// ============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key 键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** @param key 键* @param value 值* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** @param key 键* @return*/public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** @param key 键* @return*/public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}// ================================Map=================================/*** HashGet** @param key 键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 通过key 获取所有的field +value** @param key* @return*/public Map<String, Object> hgetall(final String key) {return (Map<String, Object>) redisTemplate.execute(new RedisCallback() {public Map<String, Object> doInRedis(RedisConnection connection) throws DataAccessException {try {Map<byte[], byte[]> values = connection.hGetAll(key.getBytes());if (CollectionUtils.isEmpty(values)) {return new HashMap<>();} else {// 将Map<byte[],byte[]>转换为Map<String,String>Map<String, Object> allValueMap = new HashMap<>(values.size());Iterator itor = values.keySet().iterator();while (itor.hasNext()) {byte[] mapKey = (byte[]) itor.next();allValueMap.put(new String(mapKey), values.get(mapKey));}return allValueMap;}} catch (Exception e) {e.printStackTrace();} finally {}return new HashMap<>();}});}/*** 通过key 获取所有的field +value** @param key* @return*/public Map<String, String> hgetallStr(final String key) {return (Map<String, String>) redisTemplate.execute(new RedisCallback() {public Map<String, String> doInRedis(RedisConnection connection) throws DataAccessException {try {Map<byte[], byte[]> values = connection.hGetAll(key.getBytes());if (CollectionUtils.isEmpty(values)) {return new HashMap<>();} else {// 将Map<byte[],byte[]>转换为Map<String,String>Map<String, String> allValueMap = new HashMap<>(values.size());Iterator itor = values.keySet().iterator();while (itor.hasNext()) {byte[] mapKey = (byte[]) itor.next();allValueMap.put(new String(mapKey), new String(values.get(mapKey), "utf-8"));}return allValueMap;}} catch (Exception e) {e.printStackTrace();} finally {}return new HashMap<>();}});}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** @param key 键* @param map 对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key 键* @param item 项* @param value 值* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key 键 不能为null* @param item 项 可以使多个 不能为null*/public Long hdel(String key, Object... item) {Long delete = redisTemplate.opsForHash().delete(key, item);return delete;}/*** 判断hash表中是否有该项的值** @param key 键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key 键* @param item 项* @param by 要增加几(大于0)* @return*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key 键* @param item 项* @param by 要减少记(小于0)* @return*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}// ============================set=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key 键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key 键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key 键* @param time 时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0)expire(key, time);return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** @param key 键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}// ===============================list=================================/*** 获取list缓存的内容** @param key 键* @param start 开始* @param end 结束 0 到 -1代表所有值* @return*/@Deprecatedpublic List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 弹出最右边的元素,弹出之后该值在列表中将不复存在* @param key* @return*/public Object lRPop(String key) {try {return redisTemplate.opsForList().rightPop(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止* @param key* @return*/public Object lRPop(String key, long timeout, TimeUnit unit) {try {return redisTemplate.opsForList().rightPop(key,timeout,unit);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)* @param key* @param value* @return*/public boolean lLPush(String key, Object value) {try {redisTemplate.opsForList().leftPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)* @param key* @param value* @param time 失效时间* @return*/public boolean lLPush(String key, Object value, long time) {try {redisTemplate.opsForList().leftPush(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** incrby = 加指定值 ,key 不存在时候会设置 key,并认为原来的 value 是 0* @param key*/@SuppressWarnings({"unchecked", "rawtypes"})public void incrby(final String key, final long value) {try {if(key != null) {redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {Long retValue = connection.incrBy(key.getBytes(), value);return retValue;}});}} catch (Exception e) {e.printStackTrace();}}/*** 删除指定key的hash* @param key* @return* @throws Exception*/public boolean deleteKey(final String key) throws Exception {return redisTemplate.delete(key);}
}