手寫redis@Cacheable注解?支持過期時(shí)間設(shè)置方式
原理解釋
友情鏈接 手寫redis @ Cacheable注解參數(shù)java對象作為鍵值
@Cacheable注解作用,將帶有該注解方法的返回值存放到redis的的中;
使用方法在方法上使用@Cacheable(鍵=“測試+#P0 + P1#...”)
表示鍵值為測試+方法第一個(gè)參數(shù)+方法第二個(gè)參數(shù),值為該方法的返回值。
以下源代碼表示獲取人員列表,Redis的中存放的關(guān)鍵值為'領(lǐng)袖'+ leaderGroupId + UUID + yearDetailId
@Override @Cacheable(key="'leader'+#p0+#p1+#p2",value="leader") public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) { return sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId); }
等同于
@Override public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) { String key = "leader" + leaderGroupId + uuid + yearDetailId; // 判斷緩存是否存在redis中 boolean hasKey = redisUtil.hasKey(key); if (hasKey) { //如果存在 返還redis中的值 Object leadersList = redisUtil.get(key); return (List<Leader>) leadersList; } else { List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId); //將查詢結(jié)果存放在redis中 redisUtil.set(key, leadersQuotaDetailList); return leadersQuotaDetailList; } }
說白了就是在原方法的前面判斷的關(guān)鍵值是否存在的Redis的中,如果存在就取內(nèi)存中的值,如果不存在就查詢數(shù)據(jù)庫,將查詢結(jié)果存放在Redis的的中。
實(shí)現(xiàn)方法
- 使用代理模式,在方法執(zhí)行前和執(zhí)行后可以添加其他處理程序,本文采用springAOP +注解方式。
- 集成redis,封裝Redis工具類
- 原版本不支持 過期時(shí)間 設(shè)置,本文將實(shí)現(xiàn)
源代碼
緩存配置類RedisConfig
package com.huajie.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; /** * Redis緩存配置類 */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; // 自定義緩存key生成策略 @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, java.lang.reflect.Method method, Object... params) { StringBuffer sb = new StringBuffer(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } // 緩存管理器 @Bean public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); // 設(shè)置緩存過期時(shí)間 cacheManager.setDefaultExpiration(10000); return cacheManager; } @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } private void setSerializer(StringRedisTemplate template) { @SuppressWarnings({ "rawtypes", "unchecked" }) Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); } }
Redis的依賴引入,配置文件,工具類RedisUtil,網(wǎng)上幾個(gè)版本都類似,本文參考以下版本傳送門
http://chabaoo.cn/article/233562.htm
準(zhǔn)備工作做好之后開始正式編寫注解@Cacheable nextkey()用做二級緩存本文中不會(huì)用到
nextKey用法詳情> 設(shè)計(jì)模式(實(shí)戰(zhàn)) - 責(zé)任鏈模式 <
創(chuàng)建的Java的注解@ExtCacheable
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 ExtCacheable { String key() default ""; String nextKey() default ""; int expireTime() default 1800;//30分鐘 }
SpringAop切面CacheableAspect
package com.huajie.aspect; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; 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 com.huajie.annotation.ExtCacheable; import com.huajie.utils.RedisUtil; /** * redis緩存處理 * 不適用與內(nèi)部方法調(diào)用(this.)或者private */ @Component @Aspect public class CacheableAspect { @Autowired private RedisUtil redisUtil; @Pointcut("@annotation(com.huajie.annotation.ExtCacheable)") public void annotationPointcut() { } @Around("annotationPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 獲得當(dāng)前訪問的class Class<?> className = joinPoint.getTarget().getClass(); // 獲得訪問的方法名 String methodName = joinPoint.getSignature().getName(); // 得到方法的參數(shù)的類型 Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Object[] args = joinPoint.getArgs(); String key = ""; int expireTime = 1800; try { // 得到訪問的方法對象 Method method = className.getMethod(methodName, argClass); method.setAccessible(true); // 判斷是否存在@ExtCacheable注解 if (method.isAnnotationPresent(ExtCacheable.class)) { ExtCacheable annotation = method.getAnnotation(ExtCacheable.class); key = getRedisKey(args,annotation); expireTime = getExpireTime(annotation); } } catch (Exception e) { throw new RuntimeException("redis緩存注解參數(shù)異常", e); } // 獲取緩存是否存在 boolean hasKey = redisUtil.hasKey(key); if (hasKey) { return redisUtil.get(key); } else { //執(zhí)行原方法(java反射執(zhí)行method獲取結(jié)果) Object res = joinPoint.proceed(); //設(shè)置緩存 redisUtil.set(key, res); //設(shè)置過期時(shí)間 redisUtil.expire(key, expireTime); return res; } } private int getExpireTime(ExtCacheable annotation) { return annotation.expireTime(); } private String getRedisKey(Object[] args,ExtCacheable annotation) { String primalKey = annotation.key(); //獲取#p0...集合 List<String> keyList = getKeyParsList(primalKey); for (String keyName : keyList) { int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", "")); Object parValue = args[keyIndex]; primalKey = primalKey.replace(keyName, String.valueOf(parValue)); } return primalKey.replace("+","").replace("'",""); } // 獲取key中#p0中的參數(shù)名稱 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; } }
業(yè)務(wù)模塊使用方法
@Override @ExtCacheable(key = "Leaders+#p0+#p1+#p2") // 手機(jī)端獲取領(lǐng)導(dǎo)人員列表 public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) { List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId); return leadersQuotaDetailList; }
業(yè)務(wù)模塊過期時(shí)間使用方法,5分鐘過期
@Override @ExtCacheable(key = "mobileCacheFlag", expireTime = 60 * 5) public int cacheFlag() { int mobileCacheFlag = 1; mobileCacheFlag = sysIndexMapper.cacheFlag(); return mobileCacheFlag; }
Redis的的截圖
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識(shí)
這篇文章主要介紹了Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識(shí),是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09Java的Struts框架中<results>標(biāo)簽的使用方法
這篇文章主要介紹了Java的Struts框架中<results>標(biāo)簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11SpringBoot 統(tǒng)一公共返回類的實(shí)現(xiàn)
本文主要介紹了SpringBoot 統(tǒng)一公共返回類的實(shí)現(xiàn),配置后臺(tái)的統(tǒng)一公共返回類,這樣做目的是為了統(tǒng)一返回信息,文中示例代碼介紹的很詳細(xì),感興趣的可以了解一下2022-01-01解決spring?data?jpa?saveAll()?保存過慢問題
這篇文章主要介紹了解決spring?data?jpa?saveAll()保存過慢問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11SpringBoot實(shí)現(xiàn)IP地址解析的示例代碼
本篇帶大家實(shí)踐在springboot項(xiàng)目中獲取請求的ip與詳細(xì)地址,我們的很多網(wǎng)站app中都已經(jīng)新增了ip地址顯示,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01詳解SpringBoot中5種類型參數(shù)傳遞和json數(shù)據(jù)傳參的操作
當(dāng)涉及到參數(shù)傳遞時(shí),Spring?Boot遵循HTTP協(xié)議,并支持多種參數(shù)傳遞方式,這些參數(shù)傳遞方式可以根據(jù)請求的不同部分進(jìn)行分類,2023-12-12Java為什么匿名內(nèi)部類參數(shù)引用需要用final進(jìn)行修飾?
今天小編就為大家分享一篇關(guān)于Java為什么匿名內(nèi)部類參數(shù)引用需要用final進(jìn)行修飾?,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04使用Spring AntPathMatcher的doMatch方法
這篇文章主要介紹了使用Spring AntPathMatcher的doMatch方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09