Springboot利用redis缓存,结合Aop与自定义注解实现接口节流

news2025/1/13 15:40:05

接口的节流是开发过程中为了防止单一微服务模块突然遭受太多并发导致用户服务不流畅而产生的业务需求,就是实现在固定时间内访问同一个接口的次数也固定。开发过程中通常采用redis去作为缓存去快存快取,对于需求次数较多的数据可以存储在redis内部,那么我们能不能采用redis实现接口的节流呢?

一、先说说自定义注解 annotation

我们知道注解的一般结构是:

@Target({ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Repeatable
public @interface 注解名{
   类型 属性名() default 默认值,
    ...
}

Target是指定此注解能打在哪些地方,METHOD就是打在方法上,PARAMTERS就是打在参数上等等。

Retention是指定注解的生命周期,RUNTIME>CLASS>SOURCE。

Repeatable是指该注解能否在同一个类或者方法或者属性下被重复使用。

当我们将一个注解规定之后就可以直接通过@注解名去使用它了。

二、再聊一聊AOP

之前学习Spring的时候有说过aop的两种使用方法,这里主要描述的是aop的注解使用方法,首先要想使用aop我们需要拥有aop的依赖

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

之后我们来观察这个依赖jar包里的相关常用注解:

 Aspect是切面类注解,打上的类都会变成一个切面类,里面可以定义一些切点,并设置该切点的前置后置环绕等增强。

Aspect的源码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
    String value() default "";
}

补充一下切点表达式的格式:execution(切入点方法修饰符 返回值类型 类的全限定名.方法名(..) ),其中 * 代表全部。

三、应用案例:实现模拟的redis节流

首先导入相关的依赖之后,我们编写自定义注解@InCache:

// ElementType.FIELD用于属性,ElementType.PARAMETER用于方法参数,ElementType.METHOD 用于方法
@Target({ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface InCache {
    String key() default "";
    int value() default 0;
}

将来这个key与value就可以放入模拟的redis中,当然真正的redis设置setnx时会多一个过期时间的参数,这里由于是用concurrentMap模拟的redis就不添加这个时间的参数了。

然后我们书写基本的接口与其service类:

//利用自定义注解和Aop对接口进行节流
@RestController
@CrossOrigin
public class UserController {
    @Resource
    UserServiceImpl userService;

    @GetMapping("login")
    @ResponseBody
    @InCache(key = "login",value = 3)
    public String login(){
        Integer value = AopUtil.redis.get("login");
        if (value == 0)
            return "访问次数刷完了,访问被refuse";
        return "第"+(4-value)+"次访问:"+userService.login("account", "password");
    }
}


@Component
public class UserServiceImpl {

    public String login(String account, String password) {
        if (account.equals("account") & password.equals("password")) {
            System.out.println("----------user login success -------");
            return "success";
        }else
            return "fail";
    }
}

接下来我们给controller类中的login方法配置我们的aop增强方法:

@Aspect
@Component
@Slf4j
public class AopUtil {
    //代替redis
    public static ConcurrentMap<String,Integer> redis = new ConcurrentHashMap<>();

    //定义切入点并使用切面类方法代替
    @Pointcut("execution(public String com.example.aop.UserController.login())")
    public void interPoint(){}

    //利用前置增强检查注解
    @Before("interPoint()")
    public void checkAnnotation(JoinPoint joinPoint){
        //获取参数列表
        Object[] params =  joinPoint.getArgs();
        if (params.length != 0)
            System.out.println("切入方法的参数列表为:"+ Arrays.toString(Arrays.stream(params).toArray()));
        //获取方法,强行将签名对象类型转换成方法签名对象,仅用于在已知连接点类型为方法时使用
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //根据注解类型获取注解数组,这个直接就是索引加注解对象,可获取注解的属性值
        InCache annotation = method.getAnnotation(InCache.class);
        if (annotation == null){
            System.out.println("该方法没有使用InCache注解!");
            return ;
        }
        String key = annotation.key();

        if(redis.containsKey(key)){
           log.info("缓存已存在此key,将value进行减一操作");
            Integer oldValue = redis.get(key);
            if (oldValue > 0){
                redis.remove(key);
                redis.put(key,oldValue-1);
            }else {
                log.info("缓存中的此key已经为0,拒绝接口访问数据库!");
            }
        }else {
            log.info("将key 与 value 存入缓存");
            redis.put(key,annotation.value());
        }

    }

}

这样就可以实现基本的次数限制了,来看效果图:

      

 

       

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ansible剧本使用

剧本语言 剧本使用的yaml语言 yaml文件的后缀为.yml或者.yaml 使用空格做为缩进 相同层级的元素左侧对齐即可 缩进时不允许使用 Tab 键&#xff0c;只允许使用空格 创建剧本 直接编辑不存在会自动创建这个文件&#xff0c;先用touch新建也行 vim juben.yml编写剧本 hosts&am…

C语言零基础项目:2D 赛车游戏,详细思路+源码分享

目录 一、简介 二、如何建立一个地图包 三、关于碰撞图的绘制 四、游戏时的说明 五、如何更好地绘制赛场图与碰撞图&#xff1f; 游戏截图 源码下载 一、简介 此游戏是《2D 赛车》的”魔改版“——2.5D 双人赛车&#xff01; 原作实现了 2D 视角的赛车游戏&#xff0c…

关于 国产麒麟系统赋值给双精度double时乘以1.0f编译器优化 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/128459376 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

【 uniapp - 黑马优购 | 首页】小程序首页全局配置(home、网络请求、轮播图、分类...)

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc…

csdn里的KaTex 公式语法

KaTex 语法 Accents 字母各种上下标 波浪&#xff0c;箭头&#xff0c;声调等 Delimiters 分隔符 大括号&#xff0c;小括号&#xff0c;方括号 Environments 行列式里多行多列数字表达式包含 HTML Letters and Unicode 字符和 Unicode Layout 布局 Spacing 空格 Logic and Se…

Centos系统,防火墙没开,docker部署的rabbitmq不能外网访问监听端口,但别的端口都能正常访问???

真是一个神奇的问题&#xff0c;防火墙firewalld ,iptables都没开。 之前访问都正常&#xff0c;最近可能是服务器被动了。rabbitmq的相关监听接口&#xff0c;只能本机服务器连接了&#xff0c;导致设备连接不了rabbitmq组件了。 排查问题记录 1.防火墙是否开启。发现是关闭…

web仿真或实际内存分析应用及自动化方案

js 自带 GC&#xff08;垃圾回收&#xff09;机制&#xff0c;因此绝大多数 web 开发人员不会在日常开发中考虑内存情况&#xff08;包括本人&#xff09;&#xff0c;在多数业务场景中&#xff0c;这可能没有问题&#xff0c;但在一些核心web应用场景下&#xff08;比如某个页…

【Spring(一)】初识Spring(史上最详细的Spring介绍!)

文章目录前言1.初识Spring2.Spring Framework系统架构3.核心概念前言 在学习 Spring 之前&#xff0c;我们需要先知道为什么要学习它?    IT业的任何一门技术,它只有抢占了很强的市场占有率&#xff0c;才会有更多的人使用和学习它&#xff0c;Spring技术在我们Java开发界拥…

APP怎么免费接入MobPush

1、获取AppKey 申请Appkey的流程&#xff0c;请点击 http://bbs.mob.com/thread-8212-1-1.html?fromuid70819 2、下载SDK 下载解压后&#xff0c;如下图&#xff1a; 目录结构 &#xff08;1&#xff09;Sample&#xff1a;演示Demo。&#xff08;2&#xff09;SDK&#…

【C操作符】详解操作符

操作符前言一、操作符分类二、算数操作符三、移位操作符&#xff08;一&#xff09;原码、补码、反码&#xff08;二&#xff09;操作符应用1.左移操作符&#xff08;1&#xff09;正数&#xff08;2&#xff09;负数&#xff08;3&#xff09;总结2.右移操作符&#xff08;1&a…

《码出高效:java开发手册》六-数据结构与集合(二)

前言 接上篇&#xff0c;第六章第二部分&#xff0c;上篇讲到了红黑树的FixAfterInsertion方法&#xff0c;这个方法原理与fixAfterDelete类似&#xff0c;只讲这个添加时的调整方法 代码可以看到&#xff0c;调整后的根节点一定是黑色的&#xff0c;叶子节点可红可黑&#x…

Spring 之 @Import 注解使用与源码浅析

1、Import 的作用&#xff1f; 再说 Import 之前先回忆下 Component 的作用&#xff0c;在类上标注该注解&#xff0c;该类就能够被 Spring 扫描封装成 BeanDefinition 并注册到容器中。但现在需要将第三方 jar 包、或者其他路径下面的包中的类也要被扫描注册呢&#xff1f;使…

Unity 制作一个简单的星系

使用素材&#xff1a; 1.Planets with Space Background in Flat Style 2.Planet Icons 创建场景 编写脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Cytaster : MonoBehaviour {[SerializeField]private float rotate_s…

【LeetCode】矩阵置零 [M](矩阵)

73. 矩阵置零 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&a…

uni-app - 封装全局 API 调用弹框组件

uni-app - 在纯 JS 文件中调用自定义弹框组件 / 封装全局 API 调用弹框组件&#xff08;解决小程序、APP 无法使用 document.body.appendChild 插入组件节点&#xff09;适配全端 uni-app中实现一个全局弹层组件 引用超级全局组件方案 一、安装 npm install vue-inset-loade…

零入门容器云网络-9:命令行式操作tun设备介绍

已发表的技术专栏&#xff08;订阅即可观看所有专栏&#xff09; 0  grpc-go、protobuf、multus-cni 技术专栏 总入口 1  grpc-go 源码剖析与实战  文章目录 2  Protobuf介绍与实战 图文专栏  文章目录 3  multus-cni   文章目录(k8s多网络实现方案) 4  gr…

测开工具:spring boot 实现同步数据库表结构

源码&#xff1a; GitHub - 18713341733/mysqlsync 一、使用场景 一个项目&#xff0c;有多套开发环境。有一套标准的数据库&#xff0c;不同的开发环境&#xff0c;有各自的一套数据库。 标准数据库的表结构经常发生变化&#xff0c;不同的开发环境中的数据库&#xff0c;…

C#,图像二值化(04)——全局阈值 Kittler 算法及其源程序

1、Kittler算法&#xff08;最小误差法&#xff09;概述 最小误差法是 J. Kittler & J. Illingworth 1986年在《MINIMUM ERROR THRESHOLDING》文章中提出的一种基于直方图的阈值分割方法,简称 Kittler 算法。其思想:假设灰度图像由目标和背景组成,且目标和背景满足一混合高…

11个技巧让你成为更好的 Typescript 程序员

学习 Typescript 通常是一次重新发现之旅。您的最初印象可能非常具有欺骗性&#xff1a;这不就是一种注释 Javascript 的方式&#xff0c;所以编译器可以帮助我找到潜在的错误吗&#xff1f; 通过 r/mevlixreddit 虽然这句话通常是正确的&#xff0c;但随着您继续前进&#xff…

【聆思CSK6 视觉AI开发套件试用】AI识别试用以及闭坑方案

本篇文章来自极术社区与聆思科技组织的CSK6 视觉AI开发套件活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;X Y Z 非常感谢能有这次机会体验聆思CSK6 视觉AI开发套件。上班的一大早收到了快递&#xff0c;迫不及待的打开快递。必须先来个图&#xff0…