注解本身不实现限流,仅作声明标记;真正起作用的是AOP切面拦截+Redis计数器(或Guava令牌桶)+动态key生成(IP/用户ID/接口路径),配合Lua脚本保证原子性,超限时抛异常或返回429。

Java 中注解本身不能直接实现接口防刷与限流,它只是元数据标记;真正起作用的是配合 AOP(如 Spring AOP 或 AspectJ)+ 限流算法(如令牌桶、滑动窗口)+ 存储(如 Redis 或本地缓存)的一套拦截与判断逻辑。注解的作用是声明“这个接口需要被限流”,由切面统一处理。
定义自定义限流注解
先创建一个运行时保留的注解,用于标注在 Controller 方法上:
@Target(ElementType.METHOD)<br>@Retention(RetentionPolicy.RUNTIME)<br>@Documented<br>public @interface RateLimit {<br> int value() default 100; // 每秒允许请求数(QPS)<br> int timeout() default 1; // 超时时间(秒),用于 Redis key 过期<br> String key() default ""; // 自定义限流 Key(可选,如 "user:{uid}")<br> String msg() default "请求过于频繁,请稍后再试";<br>}
这个注解不带逻辑,只提供配置参数,便于后续切面读取。
立即学习“Java免费学习笔记(深入)”;
编写 AOP 切面拦截限流逻辑
用 @Aspect 定义切面,在方法执行前检查是否超过阈值:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 从注解中提取 QPS、key 模板、超时等参数
- 动态生成限流唯一标识(如:
ip:192.168.1.100:method:/api/order或结合用户 ID) - 调用限流器(如基于 Redis 的 Lua 脚本实现原子计数 + 过期)
- 若超出限制,抛出异常或返回固定响应(如 HTTP 429)
示例核心逻辑(使用 RedisTemplate + Lua):
// Lua 脚本:原子性地 incr 并设置过期<br>String script = "local current = redis.call('incr', KEYS[1])\n" +<br> "if current == 1 then\n" +<br> " redis.call('expire', KEYS[1], ARGV[1])\n" +<br> "end\n" +<br> "return current";<br>Long count = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),<br> Collections.singletonList(key), timeout + "");<br>if (count > limit) {<br> throw new RuntimeException("触发限流");<br>}
支持多种限流策略与 Key 构建方式
实际项目中需灵活适配不同维度的限流:
- IP 级限流:从 Request 获取客户端真实 IP(注意代理头)
- 用户级限流:从 token 解析 userId 或从 Session 取 uid
-
接口级限流:用
request.getRequestURL() + request.getMethod() -
组合 Key:如
"rate:ip:" + ip + ":uri:" + uri,避免全局一刀切
可在注解的 key() 属性中写 SpEL 表达式(如 "#p0.userId"),再用 EvaluationContext 解析,提升复用性。
补充:集成 Sentinel 或 Resilience4j 更省心
若项目已引入高可用组件,不建议重复造轮子:
-
Sentinel:通过
@SentinelResource+ 规则配置实现限流,支持控制台动态调整 -
Resilience4j:用
@RateLimiter注解,底层基于滑动窗口,轻量无依赖 - 两者都支持与 Spring Boot 自动装配,且提供熔断、降级等扩展能力
自研方案适合学习原理或对性能/存储有强定制需求(如必须用本地 Caffeine 缓存 + Guava RateLimiter);生产环境推荐成熟框架。

















