预防缓存穿透工具类

news2024/11/19 10:28:54

1. 前言

缓存穿透大家都知道,这里简单过一下

缓存和数据库中都没有的数据,而用户不断发起请求。比如查询id = -1 的值

想着很多面向C端的查询接口,可能都需要做一下缓存操作,这里简单写了个自定义注解,将查询结果(包含null值)做个缓存

这个只能预防单秒内接口高频次请求,要是一直搞随机值请求这个只能采取其他手段处理了(比如IP拉黑什么的…)

工具类留底,以后兴许可以直接抄~( ̄▽ ̄)"

2. 正文

直接上代码了

2.1 自定义注解

CacheResult

import java.lang.annotation.*;

/**
 * <pre>
 * 接口缓存
 * 根据接口的第一个入参对象,和返回值进行缓存
 * 缓存的前缀为 TEMPORARY_CACHE:类名:方法名:key值
 * 示例:@CacheResult(key="userId + '_' + ecommerceId", seconds = 2L)
 * 缓存的key:TEMPORARY_CACHE:EcommerceController:getOrderList:407622341504839680_527203683850731520
 * </pre>
 * @author weiheng
 * @date 2023-08-25
 **/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface CacheResult {

    /** 入参支持 SpEL表达式 做参数提取,比如入参对象有属性userId和ecommerceId -> key="userId + '_' + ecommerceId" */
    String key();

    /** 缓存时长,单位:秒 */
    long seconds();
}

2.2 统一做缓存处理的切面

CacheAspect

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RBucket;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 缓存统一处理
 * @author weiheng
 * @date 2023-08-25
 **/
@Slf4j
@Aspect
@Component
public class CacheAspect {

    /** 临时缓存的统一前缀 */
    public static final String DEFAULT_PREFIX = "TEMPORARY_CACHE:";
    /** 缓存分隔符 */
    public static final String DELIMITER = ":";

    @Resource
    private RedissonHelper redissonHelper;

    /**
     * 拦截通知
     *
     * @param proceedingJoinPoint 入参
     * @return Object
     */
    @Around("@annotation(cacheSeconds)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, CacheResult cacheSeconds) {

        Object[] args = proceedingJoinPoint.getArgs();
        if (args.length == 0) {
            // 方法没有入参,不做缓存
            return proceed(proceedingJoinPoint);
        }
        // 1. 判断缓存中是否存在,有则直接返回
        Object firstArg = args[0];
        // 获取用户指定的参数
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext(firstArg);
        Expression keyExpression = parser.parseExpression(cacheSeconds.key());
        String businessKey = keyExpression.getValue(context, String.class);
        // 拼装缓存key
        String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
        String methodName = proceedingJoinPoint.getSignature().getName();
        String prefix = className + DELIMITER + methodName;
        String cacheKey = DEFAULT_PREFIX + prefix + DELIMITER + businessKey;

        RBucket<?> bucket = redissonHelper.getBucket(cacheKey);
        boolean exists = bucket.isExists();
        if (exists) {
            // 缓存中有值,直接返回
            return bucket.get();
        }

        // 2. 执行方法体
        Object returnValue = proceed(proceedingJoinPoint);
        // 3. 做个N秒的缓存
        long seconds = cacheSeconds.seconds();
        redissonHelper.setValueAndSeconds(cacheKey, returnValue, seconds);
        return returnValue;
    }

    private Object proceed(ProceedingJoinPoint proceedingJoinPoint) {
        Object returnValue;
        try {
            returnValue = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            log.error("error msg:", e);
            if (e instanceof SystemException) {
                throw (SystemException) e;
            }
            throw new SystemException(e.getMessage());
        }
        return returnValue;
    }

3. 使用示例

原本定义个2秒就OK了,这里为了方便看测试结果,给了60秒

@CacheResult(key=“userId + ‘_’ + ecommerceId”, seconds = 60L)

redis缓存如下:
在这里插入图片描述
就到这里了

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

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

相关文章

黄金现货价格强弱怎么看?

相对强弱指标RSI由韦尔斯.怀尔德(Welles Wilder)提出&#xff0c;最初被用于期货交易之中&#xff0c;后来人们发现该指标用在现货黄金市场上的效果也十分不错&#xff0c;并对它的特点不断进行归纳和总结&#xff0c;如今已经成为市场上被应用得最为广泛的技术指标之一。 我们…

【C++】GCC对应C++的版本支持

1、查看当前GCC的版本 pffNUC12WSKi7:~$ gcc -v Using built-in specs. COLLECT_GCCgcc COLLECT_LTO_WRAPPER/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper OFFLOAD_TARGET_NAMESnvptx-none:hsa OFFLOAD_TARGET_DEFAULT1 Target: x86_64-linux-gnu Configured with: ../src/co…

高忆管理股市资讯:保持合理息差水平 新一轮存款利率下调可期

1年期借款商场报价利率(LPR)日前再度下调&#xff0c;意味着实体经济融资本钱将随之下行。业内人士以为&#xff0c;当时商业银行负债端本钱仍显刚性&#xff0c;净息差已降至近年来低位&#xff0c;为保持合理息差水平&#xff0c;商业银行存款挂牌利率或许迎来新一轮下调。 上…

实训笔记8.25

实训笔记8.25 8.25笔记一、Flume数据采集技术1.1 Flume实现数据采集主要借助Flume的组成架构1.2 Flume采集数据的时候&#xff0c;核心是编写Flume的采集脚本xxx.conf1.2.1 脚本文件主要由五部分组成 二、Flume案例实操2.1 采集一个网络端口的数据到控制台2.1.1 分析案例的组件…

NFT Insider #104:The Sandbox:全新土地销售活动 Turkishverse 来袭

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据&#xff0c;艺术新闻类&#xff0c;游戏新闻类&#xff0c;虚拟世界类&#…

校园供水系统智能管理

import pandas as pd data1pd.read_excel("C://Users//JJH//Desktop//E//附件_一季度.xlsx") data2pd.read_excel("C://Users//JJH//Desktop//E//附件_二季度.xlsx") data3pd.read_excel("C://Users//JJH//Desktop//E//附件_三季度.xlsx") data4…

Unity3D Pico VR 手势识别 二

Unity3D Pico VR 手势识别_Cool-浩的博客-CSDN博客 此篇主要讲解怎么手势追踪&#xff0c;手势姿态自定义预制识别&#xff0c;不会导入SDK和配置环境的请看上一章节 环境要求 SDK 版本&#xff1a;2.3.0 及以上PICO 设备型号&#xff1a;PICO Neo3 和 PICO 4 系列PICO 设备系…

Elasticsearch 集成---Spark Streaming 框架集成

一.Spark Streaming 框架介绍 Spark Streaming 是 Spark core API 的扩展&#xff0c;支持实时数据流的处理&#xff0c;并且具有可扩展&#xff0c; 高吞吐量&#xff0c;容错的特点。 数据可以从许多来源获取&#xff0c;如 Kafka &#xff0c; Flume &#xff0c; Kin…

网络地址转换NAT-动态NAT的使用范围和配置-思科EI,华为数通

网络地址转换NAT-动态NAT的使用范围和配置 什么是动态NAT&#xff1f; 使用公有地址池&#xff0c;并以先到先得的原则分配这些地址。当具有私有 IP 地址的主机请求访问 Internet 时&#xff0c;动态 NAT 从地址池中选择一个未被其它主机占用的 IP 地址一对一的转化。当数据会话…

操作无法完成错误0x0000709的解决方法分享,教你快速修复错误代码问题

在使用计算机时&#xff0c;我们有时会遇到各种错误代码。其中之一是错误代码0x0000709&#xff0c;表示操作无法完成。这个错误代码可能由多种原因引起&#xff0c;但幸运的是&#xff0c;我们可以采取一些措施来解决它。本文将介绍错误代码0x0000709的含义&#xff0c;提供几…

使用vlc在线播放rtsp视频url

1. 2. 3. 工具链接&#xff1a; https://download.csdn.net/download/qq_43560721/88249440

人效九宫格城市沙龙暨《人效九宫格白皮书》发布会 —上海站,圆满结束

8月11日&#xff0c;在上海龙之梦万丽酒店&#xff0c;由盖雅工场主办的人效九宫格城市沙龙暨《人效九宫格白皮书》发布会 —上海站&#xff0c;圆满结束。 近百位来自多个行业的企业管理者及人力资源从业者汇聚一堂&#xff0c;共同探讨企业如何将盈利模式从数量增长转为质量增…

生成式 AI 在 Gartner 的 2023 年炒作周期中备受关注

原创 | 文 BFT机器人 01 背景 Gartner&#xff0c; Inc. 在其最新的 2023 年新兴技术炒作周期中&#xff0c;将生成人工智能(AI)定位于膨胀期望的顶峰&#xff0c;预计它将在未来两到五年内带来转型效益。这种人工智能变体是更广泛的新兴人工智能趋势的一部分&#xff0c;预示…

挂耳式运动耳机哪个款式戴着跑步舒服、挂耳式运动耳机推荐

对于和我一样热爱健身和运动的人来说&#xff0c;音乐就像一种调动情绪的"兴奋剂"&#xff0c;在戴上耳机、聆听着动感的音乐时&#xff0c;我们能够感受到肌肉的收缩&#xff0c;完全沉浸在自己的世界中。这种状态让我们的训练状态达到巅峰&#xff0c;快乐倍增。因…

暖手宝方案

充电暖手宝因为它的便携性&#xff0c;既能供暖又能当充电宝使用而备受人们喜爱。航誉微推出充电暖手宝方案&#xff0c;主控芯片为航誉微单片机HU系列&#xff0c;具有智能温控功能&#xff0c;可定制冷光显示屏。一、暖手宝方案原理 目前&#xff0c;市场常见的暖手宝大致有三…

大数据领域都有什么发展方向

近年来越来越多的人选择大数据行业&#xff0c;大数据行业前景不错薪资待遇好&#xff0c;各大名企对于大数据人才需求不断上涨。 大数据从业领域很宽广&#xff0c;不管是科技领域还是食品产业&#xff0c;零售业等都是需要大数据人才进行大数据的处理&#xff0c;以提供更好…

钡铼技术BL102 PLC网关案例:远程调试西门子PLC程序

网口PLC 远程下载 1、打开网关配置软件&#xff0c;点击“搜索”&#xff0c;搜索局内网网关BL102 ​2、搜索到的网关设备&#xff0c;选择要配置的设备&#xff0c;双击登录 ​3、输入登录密码登录&#xff0c;默认是123456 ​4、配置网关网口采集PLC&#xff0c;远程下载暂…

行业首家·合规典范|昂首资本携手菲律宾警察局,树立经纪商合规经营典范

Anzo Capital 昂首资本携手菲律宾达沃市警察局长阿尔贝托P卢帕兹受邀参加由 AFP-PNP Southern Mindanao Press Corps( 菲律宾武装部队(AFP)和菲律宾国家警察(PNP)南部棉兰老岛记者团)举办的新闻发布会。 本次新闻发布会在菲律宾达沃市皇家曼达亚酒店举行&#xff0c;Anzo Cap…

STM32 + RTThread + UGUI

一、概述 开发板&#xff1a;STM32F103C8T6显示器&#xff1a;ST7735SRT-Thread&#xff1a;5.0.0 玩过 GUI 的小伙伴都知道&#xff0c;界面的显示是一个个像素点组合起来的&#xff0c;那么直接构建出来炫酷的 GUI 还是相对比较困难的&#xff0c;所以我们一般都会使用一些…

反射机制-体会反射的动态性案例(尚硅谷Java学习笔记)

// 举例01 public class Reflect{ // 静态性 public Person getInstance(){return new Person(); }// 动态性 public T<T> getInstance(String className) throws Exception{Calss clzz Class.forName(className);Constructor con class.getDeclaredConstructor();con…