日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot中通过自定义缓存注解(AOP切面拦截)实现数据库数据缓存到Redis

發布時間:2025/3/19 javascript 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot中通过自定义缓存注解(AOP切面拦截)实现数据库数据缓存到Redis 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

場景

若依前后端分離版本地搭建開發環境并運行項目的教程:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662

基于上面搭建起來前后端分離的Vue+SpringBoot的項目。

其中數據庫使用的是Mysql,緩存層用的是Redis。

數據庫中某個表存儲的信息,在業務代碼比如定時任務中,需要頻繁的查詢。

所以將表中的數據存儲到redis中一份。

其原理是,在調用查詢方法時,判斷redis中是否已經有,如果有則優先從redis中查詢。

如果沒有則在數據庫中查詢后并存入到Redis中一份,并給其設置過期時間。

這樣在過期時間之內,查詢數據會從redis中查詢,過期之后會重新從Mysql中查詢并存入到Redis一份。

并且還要實現,再對這個Mysql表進行新增、編輯、刪除的操作時,將redis中存儲的數據

進行刪除,這樣下次查詢就會查詢數據庫中最新的。

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。

實現

首先在Mysql中新建一個表bus_student

然后基于此表使用代碼生成,前端Vue與后臺各層代碼生成并添加菜單。

然后來到后臺代碼中,在后臺框架中已經添加了操作redis的相關依賴和工具類。

但是這里還需要添加aspect依賴

??????? <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.3.14.RELEASE</version></dependency>

然后在存放配置類的地方新建新增redis緩存的注解

package com.ruoyi.system.redisAop;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/** @Author* @Description 新增redis緩存**/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AopCacheEnable {//redis緩存keyString[] key();//redis緩存存活時間默認值(可自定義)long expireTime() default 3600;}

以及刪除redis緩存的注解

package com.ruoyi.system.redisAop;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/** @Description 刪除redis緩存注解**/ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AopCacheEvict {//redis中的key值String[] key(); }

然后再新建一個自定義緩存切面具體實現類CacheEnableAspect

存放位置

package com.ruoyi.system.redisAop;import com.ruoyi.system.domain.BusStudent; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit;/** @Description 自定義緩存切面具體實現類**/ @Aspect @Component public class CacheEnableAspect {@Autowiredpublic RedisTemplate redisCache;/*** Mapper層切點 使用到了我們定義的 AopCacheEnable 作為切點表達式。*/@Pointcut("@annotation(com.ruoyi.system.redisAop.AopCacheEnable)")public void queryCache() {}/*** Mapper層切點 使用到了我們定義的 AopCacheEvict 作為切點表達式。*/@Pointcut("@annotation(com.ruoyi.system.redisAop.AopCacheEvict)")public void ClearCache() {}@Around("queryCache()")public Object Interceptor(ProceedingJoinPoint pjp) {Object result = null;//注解中是否有#標識boolean spelFlg = false;//判斷是否需要走數據庫查詢boolean selectDb = false;//redis中緩存的keyString redisKey = "";//獲取當前被切注解的方法名Method method = getMethod(pjp);//獲取當前被切方法的注解AopCacheEnable aopCacheEnable = method.getAnnotation(AopCacheEnable.class);//獲取方法參數值Object[] arguments = pjp.getArgs();//從注解中獲取字符串String[] spels = aopCacheEnable.key();for (String spe1l : spels) {if (spe1l.contains("#")) {//注解中包含#標識,則需要拼接spel字符串,返回redis的存儲redisKeyredisKey = spe1l.substring(1) + arguments[0].toString();} else {//沒有參數或者參數是List的方法,在緩存中的keyredisKey = spe1l;}//取出緩存中的數據result = redisCache.opsForValue().get(redisKey);//緩存是空的,則需要重新查詢數據庫if (result == null || selectDb) {try {result =? pjp.proceed();//從數據庫查詢到的結果不是空的if (result != null && result instanceof ArrayList) {//將redis中緩存的結果轉換成對象listList<BusStudent> students = (List<BusStudent>) result;//判斷方法里面的參數是不是BusStudentif (arguments[0] instanceof BusStudent) {//將rediskey-students 存入到redisredisCache.opsForValue().set(redisKey, students, aopCacheEnable.expireTime(), TimeUnit.SECONDS);}}} catch (Throwable e) {e.printStackTrace();}}}return result;}/*** 定義清除緩存邏輯,先操作數據庫,后清除緩存*/@Around(value = "ClearCache()")public Object evict(ProceedingJoinPoint pjp) throws Throwable {//redis中緩存的keyMethod method = getMethod(pjp);// 獲取方法的注解AopCacheEvict cacheEvict = method.getAnnotation(AopCacheEvict.class);//先操作dbObject result = pjp.proceed();// 獲取注解的key值String[] fieldKeys = cacheEvict.key();for (String spe1l : fieldKeys) {//根據key從緩存中刪除redisCache.delete(spe1l);}return result;}/*** 獲取被攔截方法對象*/public Method getMethod(ProceedingJoinPoint pjp) {Signature signature = pjp.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method targetMethod = methodSignature.getMethod();return targetMethod;} }

注意這里的queryCache和ClearCache,里面切點表達式

分別對應上面自定義的兩個AopCacheEnable和AopCacheEvict。

然后在環繞通知的queryCache方法執行前后時

獲取被切方法的參數,參數中的key,然后根據key去redis中去查詢,

如果查不到,就把方法的返回結果轉換成對象List,并存入到redis中,

如果能查到,則將結果返回。

然后找到這個表的查詢方法,mapper層,比如要將查詢的返回結果存儲進redis

??? @AopCacheEnable(key = "BusStudent",expireTime = 40)public List<BusStudent> selectBusStudentList(BusStudent busStudent);

然后在這個表的新增、編輯、刪除的mapper方法上添加

??? /*** 新增學生** @param busStudent 學生* @return 結果*/@AopCacheEvict(key = "BusStudent")public int insertBusStudent(BusStudent busStudent);/*** 修改學生** @param busStudent 學生* @return 結果*/@AopCacheEvict(key = "BusStudent")public int updateBusStudent(BusStudent busStudent);/*** 刪除學生** @param id 學生ID* @return 結果*/@AopCacheEvict(key = "BusStudent")public int deleteBusStudentById(Integer id);

注意這里的注解上的key要和上面的查詢的注解的key一致。

然后啟動項目,如果啟動時提示:

Consider marking one of the beans as @Primary, updating the consumer to acce

因為sringboot通過@Autowired注入接口的實現類時發現有多個,也就是有多個類繼承了這個接口,spring容器不知道使用哪一個。

找到redis的配置類,在RedisTemplate上添加@Primary注解

驗證注解的使用

debug啟動項目,在CacheEnableAspect中查詢注解中打斷點,然后調用查詢方法,

就可以看到能進斷點,然后就可以根據自己想要的邏輯和效果進行修改注解。

第一次查詢時redis中是沒有的,所以會走mysql查詢,在過期時間之內就不再查詢mysq,而是查詢redis了。

然后再調用新增、編輯、刪除接口時會將redis中緩存的數據刪掉。

但是使用若依這套框架,在新增、編輯、刪除操作后會調用查詢接口,所以會直接又存儲進來。

所以可以用postman等接口測試工具測試。

然后就是當操作完之后如果redis中的數據還沒過期,前端頁面查詢的仍然是redis中的數據,不是最新數據。

所以redis中過期的時間自己把握。

另外此種緩存機制,建議不要和前端請求的mapper進行混用。

建議自定義新的mapper只取用需要的數據,然后給其他比如高頻率的定時任務查詢用。

總結

以上是生活随笔為你收集整理的SpringBoot中通过自定义缓存注解(AOP切面拦截)实现数据库数据缓存到Redis的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。