手写redis@Cacheable注解 支持过期时间设置方式

目录
  • 原理解释
  • 实现方法
  • 源代码

原理解释

友情链接  手写redis @ Cacheable注解参数java对象作为键值

@Cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;

使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#...”)

表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值。

以下源代码表示获取人员列表,Redis的中存放的关键值为'领袖'+ 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);
                        //将查询结果存放在redis中
			redisUtil.set(key, leadersQuotaDetailList);
			return leadersQuotaDetailList;
		}
	}

说白了就是在原方法的前面判断的关键值是否存在的Redis的中,如果存在就取内存中的值,如果不存在就查询数据库,将查询结果存放在Redis的的中。

实现方法

  • 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springAOP +注解方式。
  • 集成redis,封装Redis工具类
  • 原版本不支持 过期时间 设置,本文将实现

源代码

缓存配置类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);
		// 设置缓存过期时间
		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,网上几个版本都类似,本文参考以下版本传送门

https://www.jb51.net/article/233562.htm

准备工作做好之后开始正式编写注解@Cacheable nextkey()用做二级缓存本文中不会用到

nextKey用法详情> 设计模式(实战) - 责任链模式 <

创建的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缓存处理
 * 不适用与内部方法调用(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 {
		// 获得当前访问的class
		Class<?> className = joinPoint.getTarget().getClass();
		// 获得访问的方法名
		String methodName = joinPoint.getSignature().getName();
		// 得到方法的参数的类型
		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缓存注解参数异常", e);
		}
		// 获取缓存是否存在
		boolean hasKey = redisUtil.hasKey(key);
		if (hasKey) {
			return redisUtil.get(key);
		} else {
                         //执行原方法(java反射执行method获取结果)
			Object res = joinPoint.proceed();
                         //设置缓存
			redisUtil.set(key, res);
                         //设置过期时间
			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中的参数名称
	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;
	}
}

业务模块使用方法

@Override
	@ExtCacheable(key = "Leaders+#p0+#p1+#p2")
	// 手机端获取领导人员列表
	public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {
		List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);
		return leadersQuotaDetailList;
	}

业务模块过期时间使用方法,5分钟过期

@Override
	@ExtCacheable(key = "mobileCacheFlag", expireTime = 60 * 5)
	public int cacheFlag() {
		int mobileCacheFlag = 1;
		mobileCacheFlag = sysIndexMapper.cacheFlag();
		return mobileCacheFlag;
	}

Redis的的截图

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java操作Redis缓存设置过期时间的方法

    关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息转为json串作为value保存在redis中,通常做法是: //生成token String token = UUID.randomUUID().toString(); //把用户信息写入redis jedisClient.set(REDIS_USER_SESSION_KEY + ":"

  • 详解SpringBoot2.0的@Cacheable(Redis)缓存失效时间解决方案

    问题   @Cacheable注解不支持配置过期时间,所有需要通过配置CacheManneg来配置默认的过期时间和针对每个类或者是方法进行缓存失效时间配置. 解决   可以采用如下的配置信息来解决的设置失效时间问题 配置信息 @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return new RedisCacheManager( RedisCacheWriter.no

  • Redis有效时间设置以及时间过期处理操作

    本文对redis的过期处理机制做个简单的概述,让大家有个基本的认识. Redis中有个设置时间过期的功能,即对存储在redis数据库中的值可以设置一个过期时间.作为一个缓存数据库,这是非常实用的.如我们一般项目中的token或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能. 一.有效时间设置: redis对存储值的过期处理实际上是针对该值的键(key)处理的,即时间的设置也是设置key的有效时间.Expires字典保存

  • Spring @Cacheable指定失效时间实例

    目录 Spring @Cacheable指定失效时间 新版本配置 老版本配置 @Cacheable缓存失效时间策略默认实现及扩展 背景 Spring Cache Redis实现 Spring Cache 失效时间自行刷新 Spring @Cacheable指定失效时间 新版本配置 @Configuration @EnableCaching public class RedisCacheConfig { @Bean public RedisCacheManagerBuilderCustomizer

  • SpringBoot配置Redis自定义过期时间操作

    SpringBoot配置Redis自定义过期时间 Redis配置依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> <version>1.4.4.RELEASE</version> </dependency> <dependency>

  • 手写redis@Cacheable注解 支持过期时间设置方式

    目录 原理解释 实现方法 源代码 原理解释 友情链接  手写redis @ Cacheable注解参数java对象作为键值 @Cacheable注解作用,将带有该注解方法的返回值存放到redis的的中; 使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#...”) 表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值. 以下源代码表示获取人员列表,Redis的中存放的关键值为'领袖'+ leaderGroupId + UUID + yearDetailId @

  • 手写redis@Cacheable注解 参数java对象作为key值详解

    目录 1.实现方式说明 1.1问题说明 1.2实现步骤 2.源代码 3.测试 1.实现方式说明 本文在---- 手写redis @ Cacheable注解支持过期时间设置   的基础之上进行扩展. 1.1问题说明 @ Cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一个参数,#p1表示第二个参数,以此类推. 目前方法的第一个参数为Java的对象,但是原注解只支持Java的的基本数据类型. 1.2实现步骤 1.在原注解中加入新的参数,

  • localStorage过期时间设置的几种方法

    目录 问题描述 1.初级解法 2.中级解法 3.高级解法 4.骨灰级解法 聊到 localStorage 想必熟悉前端的朋友都不会陌生, 我们可以使用它提供的 getItem, setItem, removeItem, clear 这几个 API 轻松的对存储在浏览器本地的数据进行**「读,写, 删」操作, 但是相比于 cookie, localStorage 唯一美中不足的就是「不能设置每一个键的过期时间」**. localStorage 属性允许我们访问一个 Document 源(origi

  • Java手写Redis服务端的实现

    目录 零,起因 一,redis通讯与Netty 1,tcp 2,协议 3,编解码 4,命令处理 二,redis 的数据结构 1,底层主结构 2,key 3,list 4,set 5,hash 6,zset 三,redis AOF 持久化 1,aof线程与tcp线程解耦,即写缓冲 2,aof持久化协议 3,aof的加载与存储实现 4,内存文件映射与面向对象 四,redis 的集群特性 1,主从 2,主从复制 3,分片集群 五,redis 的压测与调优 1,aof内存泄漏 2,内存复用提升性能 3,

  • 浅谈redis的过期时间设置和过期删除机制

    目录 一:设置过期时间 二:保存过期时间 三:移除过期时间 四:计算并返回剩余生存时间 五:过期键的删除策略 六:redis使用的策略 一:设置过期时间 redis有四种命令可以用于设置键的生存时间和过期时间: EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒 PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒 EXPIREAT <KEY> <timestamp> :将键的过期时间设为

  • php中session过期时间设置及session回收机制介绍

    网上很多人给出了解答:修改配置文件中的session.gc_maxlifetime.如果想了解更多session回收机制,继续阅读.(本文环境php5.2) 概述:每一次php请求,会有1/100的概率(默认值)触发"session回收".如果"session回收"发生,那就会检查/tmp/sess_*的文件,如果最后的修改时间到现在超过了1440秒(gc_maxlifetime的值),就将其删除,意味着这些session过期失效. 1. session在端(一般是

  • redis中的配置以及密码设置方式

    目录 前言 参数介绍 bind protected-mode requirepass 总结 上线部署 线下调试 前言 redis默认情况下是没有密码的,这很容易导致服务器被攻击,被挖矿! 今天就给大家简单讲解一下自己在配置redis过程中所学习的,方便大家以后快速的上手. 注意:如果想快速配置则不需要看参数介绍,直接看总结!!! 参数介绍 redis中主要有三个参数来进行安全控制的,也是我们最常用的三个. bind ①这个参数默认值是127.0.0.1,也就是只允许redis所在机器访问redi

  • SpringBoot使用@Cacheable时设置部分缓存的过期时间方式

    目录 使用@Cacheable时设置部分缓存的过期时间 业务场景 添加Redis配置类RedisConfig.java @Cacheable自定义缓存过期时间 pom yml RedisConfig CustomRedisCacheManager 使用 使用@Cacheable时设置部分缓存的过期时间 业务场景 Spring Boot项目中有一些查询数据需要缓存到Redis中,其中有一些缓存是固定数据不会改变,那么就没必要设置过期时间.还有一些缓存需要每隔几分钟就更新一次,这时就需要设置过期时间

  • java简单手写版本实现时间轮算法

    时间轮 关于时间轮的介绍,网上有很多,这里就不重复了 核心思想 一个环形数组存储时间轮的所有槽(看你的手表),每个槽对应当前时间轮的最小精度 超过当前时间轮最大表示范围的会被丢到上层时间轮,上层时间轮的最小精度即为下层时间轮能表达的最大时间(时分秒概念) 每个槽对应一个环形链表存储该时间应该被执行的任务 需要一个线程去驱动指针运转,获取到期任务 以下给出java 简单手写版本实现 代码实现 时间轮主数据结构 /** * @author apdoer * @version 1.0 * @date

  • redis 设置生存和过期时间的原理分析

    目录 在了解原理前 先来看使用方法 原理 过期键的判定 Redis的过期键删除策略原理 在了解原理前 先来看使用方法 通过EXPIRE命令或者PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间,在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键. SETEX命令可以在设置一个字符串键的同时为键设置过期时间(只能用于字符串键) 与EXPIRE命令和PEXPIRE命令类似,客户端可以通过EXPIREAT命令或PEXPIREAT命令,以秒或者毫秒精度给数据库中

随机推荐