注解 + 拦截器:?秒防刷新
小工具篇:工具许多都是我以前在 github 之类开源平台找到的小工具类,作者的信息什么的许多都忘了。先说声不好意思了。若有相关信息,麻烦提醒一下~
解释
所谓的?秒防刷新,其实就是限制用户在某个时间内对某个 Controller 的访问时间限制。最常见的,比如学校教务系统(正方)的3s防刷新。虽然我不知道正方系统具体是如何实现的,不过可以通过 注解+拦截器 来实现。
前期准备
关于 注解+拦截器,我在上一篇小工具中已经有所介绍。
同时,关于系统轮询的问题,可以使用 @Scheduled 来进行一个全系统记录的轮询,但真的没必要……我觉得可以使用 java 自带的定时器小工具 Timer
与 TimerTask
。
* Timer 是一种定时器工具,用来在一个后台线程计划执行指定任务。
* TimerTask 一个抽象类,它的子类或者重写run方法代表一个可以被Timer计划的任务
参考资料:(Timer与TimerTask详解)http://blog.csdn.net/ahxu/article/details/249610
正式开工
- 自定义异常类
HttpServletException : 用于记录http请求或响应异常。
RequestLimitException : 用于记录用户HTTP请求超出设定的限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class RequestLimitException extends Exception { public RequestLimitException() { super ( "HTTP请求超出设定的限制" ); } public RequestLimitException(String message) { super (message); } public RequestLimitException(String message, Throwable cause){ super (message, cause); } } |
- 注解类 RequestLimit.java
1 2 3 4 5 6 7 8 9 10 11 12 | @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.METHOD) @Documented public @interface RequestLimit { //允许访问的次数,默认值MAX_VALUE int count() default Integer.MAX_VALUE; // 时间段,单位为毫秒,默认值一分钟 long time() default 60000 ; } |
- 拦截器 RequestLimitContract.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | @Aspect @Component public class RequestLimitContract { private static final Logger logger = LoggerFactory.getLogger(RequestLimitContract. class ); //用于存储记录 private Map<String, Integer> redisTemplate= new HashMap<String,Integer>(); @Before ( "within(@org.springframework.stereotype.Controller *) && @annotation(limit)" ) public void requestLimit( final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException { try { //获取 HttpServletRequest 参数 Object[] args = joinPoint.getArgs(); HttpServletRequest request = null ; for ( int i = 0 ; i < args.length; i++) { if (args[i] instanceof HttpServletRequest) { request = (HttpServletRequest) args[i]; break ; } } if (request == null ) { logger.error( "方法中缺失HttpServletRequest参数" ); throw new HttpServletException( "方法中缺失HttpServletRequest参数" ); } String ip = request.getLocalAddr(); String url = request.getRequestURL().toString(); final String key = "req_limit_" .concat(url).concat(ip); System.out.println( "ip = " +ip+ "\n" + " url = " +url+ "\n" + " key = " +key); if (redisTemplate.get(key)== null || redisTemplate.get(key)== 0 ){ redisTemplate.put(key, 1 ); } else { redisTemplate.put(key,redisTemplate.get(key)+ 1 ); } int count = redisTemplate.get(key); if (count > 0 ) { Timer timer= new Timer(); TimerTask task = new TimerTask(){ //创建一个新的计时器任务。 @Override public void run() { if (!key.equals( "" )) { redisTemplate.remove(key); } } }; timer.schedule(task, limit.time()); //安排在指定延迟后执行指定的任务。task : 所要安排的任务。limit.time() : 执行任务前的延迟时间,单位是毫秒。 } if (count > limit.count()) { logger.info( "用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]" ); throw new RequestLimitException(); } } catch (RequestLimitException e) { throw e; } catch (Exception e) { logger.error( "发生异常: " , e); } } } |
- 控制器
注意方法中一定要有 HttpServletRequest 参数!!!不然会抛出 HttpServletException 异常。
1 2 3 4 5 6 | @ResponseBody @RequestMapping ( "/hello" ) @RequestLimit (count = 10 ) public String hello(HttpServletRequest request) { return "Hello World" ; } |
效果截图
1. 正常访问
-
-
访问受限
-
服务器后台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具