惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
N
Netflix TechBlog - Medium
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
MongoDB | Blog
MongoDB | Blog
有赞技术团队
有赞技术团队
Engineering at Meta
Engineering at Meta
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
WordPress大学
WordPress大学
T
Tailwind CSS Blog
小众软件
小众软件
J
Java Code Geeks
人人都是产品经理
人人都是产品经理
博客园_首页
MyScale Blog
MyScale Blog
博客园 - 聂微东
V
Visual Studio Blog
The Cloudflare Blog
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42

博客园 - KLAPT

Spring Framework 自带工具类 Spring Web MVC 中,过滤器(Filter) 和 拦截器(Interceptor) ROW_NUMBER() 一台服务器最大能支持多少条 TCP 连接 Gateway 网关 CodeX =>Skills Redis 内存满了怎么处理 SpringBoot 默认配置修改 JWT 续签 Access Token + Refresh Token 双 Token claudeCode 命令 MyBatis 的 Mapper 接口 AI | CC GUI 集成 IDEA 完整教程 在IDEA中使用Claude Code IDEA中使用CodeX MyBatisPlus解决大数据量查询慢问题 idea 中的 claude code Token Dubbo 和 Spring Cloud Gateway的区别 Transactional 注解中propagation 掌握 Spring 框架这 10 个扩展点 SpringBoot 快速实现 api 加密 Spring Boot/Cloud 中 bootstrap.yml 与 application.yml SpringBoot 实现 DOCX 转 PDF 微服务Token鉴权设计的几种方案 进程、线程、协程 RSA 加密 Java二维码 ntp服务端和客户端 Chronyd与NTP chronyd 作为服务器时钟
SpringBoot API 接口防刷
KLAPT · 2026-09-15 · via 博客园 - KLAPT

@RequestLimit` 注解

import java.lang.annotation.*;
/**
 * 请求限制的自定义注解 * * 

@Target 注解可修饰的对象范围,ElementType.METHOD 作用于方法,ElementType.TYPE 作用于类 * (ElementType)取值有: *     

          1.CONSTRUCTOR:用于描述构造器 *     

         2.FIELD:用于描述域 *    

      3.LOCAL_VARIABLE:用于描述局部变量 *     

        4.METHOD:用于描述方法 *     

        5.PACKAGE:用于描述包 *     

       6.PARAMETER:用于描述参数 *    

    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明 * 

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃; * 而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略, * 而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。 * 使用这个meta-Annotation可以对 Annotation的“生命周期”限制。 * (RetentionPoicy)取值有: *     

       1.SOURCE:在源文件中有效(即源文件保留) *     

        2.CLASS:在class文件中有效(即class保留) *     

        3.RUNTIME:在运行时有效(即运行时保留) * * 

@Inherited * 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。 * 如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
 */

@Documented

@Inherited

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public@interface RequestLimit {

// 在 second 秒内,最大只能请求 maxCount 次

int second() default 1;

int maxCount() default 1;

}

`RequestLimitIntercept` 拦截器

自定义一个拦截器,请求之前,进行请求次数校验

自定义一个拦截器,请求之前,进行请求次数校验

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.lrshuai.limit.annotation.RequestLimit;
import top.lrshuai.limit.common.ApiResultEnum;
import top.lrshuai.limit.common.Result;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
 * 请求拦截
 */
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {

@Autowiredprivate 
RedisTemplate<String,Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**
         * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
         * isAssignableFrom()方法是判断是否为某个类的父类
         * instanceof关键字是判断是否某个类的子类
         */
if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
//HandlerMethod 封装方法定义相关的信息,如类,方法,参数等           
HandlerMethod handlerMethod = (HandlerMethod) handler;         
  Method method = handlerMethod.getMethod();
// 获取方法中是否包含注解           
RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
//获取 类中是否包含注解,也就是controller 是否有注解           
RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
// 如果 方法上有注解就优先选择方法上的参数,否则类上的参数         
  RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
if(requestLimit != null){
if(isLimit(request,requestLimit)){ 
                  resonseOut(response,Result.error(ApiResultEnum.REQUST_LIMIT));
returnfalse;                }       
    }        }
returnsuper.preHandle(request, response, handler);    }
//判断请求是否受限
public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
// 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。     
  String limitKey = request.getServletPath()+request.getSession().getId();
// 从缓存中获取,当前这个请求访问了几次       
Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
if(redisCount == null){//初始 次数           
redisTemplate.opsForValue().set(limitKey,1,requestLimit.second(), TimeUnit.SECONDS);       
}else{
if(redisCount.intValue() >= requestLimit.maxCount()){
return true;            }// 次数自增         
  redisTemplate.opsForValue().increment(limitKey);     
  }returnfalse;    }
/**
     * 回写给客户端
     * @param response
     * @param result
     * @throws IOException
     */

private void resonseOut(HttpServletResponse response, Result result) throws IOException {   
    response.setCharacterEncoding("UTF-8");     
  response.setContentType("application/json; charset=utf-8");     
  PrintWriter out = null ;     
  String json = JSONObject.toJSON(result).toString();out = response.getWriter();
out.append(json);    }}

WebMvcConfig` 配置类

因为我的是Springboot2.* 所以只需实现WebMvcConfigurer
如果是springboot1.* 那就继承自 WebMvcConfigurerAdapter
然后重写addInterceptors() 添加自定义拦截器即可。

@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private RequestLimitIntercept requestLimitIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
        log.info("添加拦截");     
  registry.addInterceptor(requestLimitIntercept);   
}}