手寫(xiě)redis@Cacheable注解?參數(shù)java對(duì)象作為key值詳解
1.實(shí)現(xiàn)方式說(shuō)明
本文在---- 手寫(xiě)redis @ Cacheable注解支持過(guò)期時(shí)間設(shè)置 的基礎(chǔ)之上進(jìn)行擴(kuò)展。
1.1問(wèn)題說(shuō)明
@ Cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一個(gè)參數(shù),#p1表示第二個(gè)參數(shù),以此類(lèi)推。
目前方法的第一個(gè)參數(shù)為Java的對(duì)象,但是原注解只支持Java的的基本數(shù)據(jù)類(lèi)型。
1.2實(shí)現(xiàn)步驟
1.在原注解中加入新的參數(shù),
objectIndexArray表示哪幾個(gè)角標(biāo)參數(shù)(從0開(kāi)始)為java對(duì)象,objectFieldArray表示對(duì)應(yīng)位置該對(duì)象的字段值作為key
2.如何獲取參數(shù)的對(duì)象以及該字段的值
使用的java的反射,拼接get方法獲取該字段值。
2.源代碼
修改java注解@ExtCacheable,本文中使用@NewCacheable
package com.huajie.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface NewCacheable { String key() default ""; int[] objectIndexArray(); String[] objectFieldArray(); int expireTime() default 1800;//30分鐘 }
SpringAop切面NewCacheableAspect
獲取AOP整體流程沒(méi)有任何變化
主要是關(guān)鍵值獲取的方式,發(fā)生了變化
使用Java的反射技術(shù)
完整代碼如下:
package com.huajie.aspect; import com.huajie.annotation.NewCacheable; import com.huajie.utils.RedisUtil; import com.huajie.utils.StringUtil; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; 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.stereotype.Component; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * redis緩存處理 不適用與內(nèi)部方法調(diào)用(this.)或者private */ @Component @Aspect @Slf4j public class NewCacheableAspect { @Autowired private RedisUtil redisUtil; @Pointcut("@annotation(com.huajie.annotation.NewCacheable)") public void annotationPointcut() { } @Around("annotationPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 獲得當(dāng)前訪問(wèn)的class Class<?> className = joinPoint.getTarget().getClass(); // 獲得訪問(wèn)的方法名 String methodName = joinPoint.getSignature().getName(); // 得到方法的參數(shù)的類(lèi)型 Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Object[] args = joinPoint.getArgs(); String key = ""; int expireTime = 3600; try { // 得到訪問(wèn)的方法對(duì)象 Method method = className.getMethod(methodName, argClass); method.setAccessible(true); // 判斷是否存在@ExtCacheable注解 if (method.isAnnotationPresent(NewCacheable.class)) { NewCacheable annotation = method.getAnnotation(NewCacheable.class); key = getRedisKey(args, annotation); expireTime = getExpireTime(annotation); } } catch (Exception e) { throw new RuntimeException("redis緩存注解參數(shù)異常", e); } log.info(key); boolean hasKey = redisUtil.hasKey(key); if (hasKey) { return redisUtil.get(key); } else { Object res = joinPoint.proceed(); redisUtil.set(key, res); redisUtil.expire(key, expireTime); return res; } } private int getExpireTime(NewCacheable annotation) { return annotation.expireTime(); } private String getRedisKey(Object[] args, NewCacheable annotation) throws Exception{ String primalKey = annotation.key(); // 獲取#p0...集合 List<String> keyList = getKeyParsList(primalKey); for (String keyName : keyList) { int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", "")); Object parValue = getParValue(annotation, keyIndex, args); primalKey = primalKey.replace(keyName, String.valueOf(parValue)); } return primalKey.replace("+", "").replace("'", ""); } private Object getParValue(NewCacheable annotation, int keyIndex, Object[] args) throws Exception{ int[] objectIndexArray = annotation.objectIndexArray(); String[] objectFieldArray = annotation.objectFieldArray(); if (existsObject(keyIndex, objectIndexArray)) { return getParValueByObject(args, keyIndex, objectFieldArray); } else { return args[keyIndex]; } } private Object getParValueByObject(Object[] args, int keyIndex, String[] objectFieldArray) throws Exception { Class cls = args[keyIndex].getClass(); Method method; if(objectFieldArray!=null&&objectFieldArray.length>=keyIndex){ method = cls.getMethod("get" + StringUtil.firstCharToUpperCase(objectFieldArray[keyIndex])); }else{ method = cls.getMethod("get" + StringUtil.firstCharToUpperCase(cls.getFields()[0].getName())); } method.setAccessible(true); log.info(method.getName()); return method.invoke(args[keyIndex]); } private boolean existsObject(int keyIndex, int[] objectIndexArray) { if (objectIndexArray == null || objectIndexArray.length <= 0) { return false; } for (int i = 0; i < objectIndexArray.length; i++) { if (keyIndex == objectIndexArray[i]) { return true; } } return false; } // 獲取key中#p0中的參數(shù)名稱(chēng) private static List<String> getKeyParsList(String key) { List<String> ListPar = new ArrayList<String>(); if (key.indexOf("#") >= 0) { int plusIndex = key.substring(key.indexOf("#")).indexOf("+"); int indexNext = 0; String parName = ""; int indexPre = key.indexOf("#"); if (plusIndex > 0) { indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+"); parName = key.substring(indexPre, indexNext); } else { parName = key.substring(indexPre); } ListPar.add(parName.trim()); key = key.substring(indexNext + 1); if (key.indexOf("#") >= 0) { ListPar.addAll(getKeyParsList(key)); } } return ListPar; } }
3.測(cè)試
業(yè)務(wù)模塊使用方法controller
@RequestMapping("queryQuotaTreeData") @ResponseBody public List<TreeNode> getTreeData() { QuotaManage quotaManage = new QuotaManage(); quotaManage.setQuotaName("測(cè)試22222"); List<TreeNode> list = this.quotaManageService.queryQuotaTreeData(quotaManage); return list; }
實(shí)現(xiàn)層objectIndexArray中的{0}表示第0個(gè)參數(shù),objectFieldArray中的“quotaName”表示對(duì)應(yīng)對(duì)象中的字段名稱(chēng)
@Override @NewCacheable(key="test+#p0",objectIndexArray = {0},objectFieldArray = {"quotaName"}) public List<TreeNode> queryQuotaTreeData(QuotaManage quotaManage) { List<TreeNode> returnNodesList = new ArrayList<TreeNode>(); List<TreeNode> nodeList = this.mapper.queryQuotaTreeData(); returnNodesList = treeUtils.getParentList(nodeList); log.info(nodeList.size()+""); return returnNodesList; }
控制臺(tái)截圖拼接的get方法名稱(chēng)和獲取的字段值
Redis的截圖
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot中使用Redisson的實(shí)現(xiàn)示例
Redission是一個(gè)強(qiáng)大的Java庫(kù),用于構(gòu)建和管理分布式系統(tǒng)中的緩存和任務(wù)調(diào)度,本文主要介紹了SpringBoot中使用Redisson的實(shí)現(xiàn)示例,感興趣的可以了解一下2023-12-12java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們深入了解java有一定的幫助,感興趣的小伙伴可以了解下2023-11-11詳解Spring Boot Admin監(jiān)控服務(wù)上下線郵件通知
本篇文章主要介紹了詳解Spring Boot Admin監(jiān)控服務(wù)上下線郵件通知,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12Java并發(fā)編程之LockSupport類(lèi)詳解
LockSupport是一種線程阻塞工具,它可以在線程內(nèi)任意位置讓線程阻塞.接下來(lái)就帶著大家詳細(xì)了解一下LockSupport類(lèi),,需要的朋友可以參考下2021-05-05Springboot自帶定時(shí)任務(wù)實(shí)現(xiàn)動(dòng)態(tài)配置Cron參數(shù)方式
這篇文章主要介紹了Springboot自帶定時(shí)任務(wù)實(shí)現(xiàn)動(dòng)態(tài)配置Cron參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11sin(x)如何求解的java代碼實(shí)現(xiàn)方法
這篇文章主要為大家介紹了sin(x)如何求解的java代碼實(shí)現(xiàn)方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04詳解maven的setting配置文件中mirror和repository的區(qū)別
這篇文章主要介紹了詳解maven的setting配置文件中mirror和repository的區(qū)別,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12