手寫(xiě)redis@Cacheable注解?支持過(guò)期時(shí)間設(shè)置方式
原理解釋
友情鏈接 手寫(xiě)redis @ Cacheable注解參數(shù)java對(duì)象作為鍵值
@Cacheable注解作用,將帶有該注解方法的返回值存放到redis的的中;
使用方法在方法上使用@Cacheable(鍵=“測(cè)試+#P0 + P1#...”)
表示鍵值為測(cè)試+方法第一個(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);
//將查詢(xún)結(jié)果存放在redis中
redisUtil.set(key, leadersQuotaDetailList);
return leadersQuotaDetailList;
}
}說(shuō)白了就是在原方法的前面判斷的關(guān)鍵值是否存在的Redis的中,如果存在就取內(nèi)存中的值,如果不存在就查詢(xún)數(shù)據(jù)庫(kù),將查詢(xún)結(jié)果存放在Redis的的中。
實(shí)現(xiàn)方法
- 使用代理模式,在方法執(zhí)行前和執(zhí)行后可以添加其他處理程序,本文采用springAOP +注解方式。
- 集成redis,封裝Redis工具類(lèi)
- 原版本不支持 過(guò)期時(shí)間 設(shè)置,本文將實(shí)現(xiàn)
源代碼
緩存配置類(lèi)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緩存配置類(lèi)
*/
@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è)置緩存過(guò)期時(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的依賴(lài)引入,配置文件,工具類(lèi)RedisUtil,網(wǎng)上幾個(gè)版本都類(lèi)似,本文參考以下版本傳送門(mén)
http://www.dhdzp.com/article/233562.htm
準(zhǔn)備工作做好之后開(kāi)始正式編寫(xiě)注解@Cacheable nextkey()用做二級(jí)緩存本文中不會(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)前訪(fǎng)問(wèn)的class
Class<?> className = joinPoint.getTarget().getClass();
// 獲得訪(fǎng)問(wèn)的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的參數(shù)的類(lèi)型
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
int expireTime = 1800;
try {
// 得到訪(fǎng)問(wèn)的方法對(duì)象
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è)置過(guò)期時(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ù)名稱(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;
}
}業(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ù)模塊過(guò)期時(shí)間使用方法,5分鐘過(guò)期
@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刪除字符串固定下標(biāo)字串的實(shí)現(xiàn)
本文主要介紹了JAVA刪除字符串固定下標(biāo)字串的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Springboot如何根據(jù)docx填充生成word文件并導(dǎo)出pdf
這篇文章主要介紹了Springboot如何根據(jù)docx填充生成word文件并導(dǎo)出pdf問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析
這篇文章主要介紹了Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析,文章通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Presto支持Elasticsearch數(shù)據(jù)源配置詳解
這篇文章主要為大家介紹了Presto支持Elasticsearch數(shù)據(jù)源配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之棧
這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之棧 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
spring boot 統(tǒng)一JSON格式的接口返回結(jié)果的實(shí)現(xiàn)
這篇文章主要介紹了spring boot 統(tǒng)一JSON格式的接口返回結(jié)果的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
SpringBoot+MyBatis簡(jiǎn)單數(shù)據(jù)訪(fǎng)問(wèn)應(yīng)用的實(shí)例代碼
這篇文章主要介紹了SpringBoot+MyBatis簡(jiǎn)單數(shù)據(jù)訪(fǎng)問(wèn)應(yīng)用的實(shí)例代碼,需要的朋友可以參考下2017-05-05
詳解基于spring多數(shù)據(jù)源動(dòng)態(tài)調(diào)用及其事務(wù)處理
本篇文章主要介紹了基于spring多數(shù)據(jù)源動(dòng)態(tài)調(diào)用及其事務(wù)處理 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06

