springboot整合redis做简单缓存
Posted 全他吗被取了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot整合redis做简单缓存相关的知识,希望对你有一定的参考价值。
一、引入依赖
<!-- 引入redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--使用fastjson做序列化和反序列化-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
二、配置redis连接
spring:
redis:
host: localhost
port: 6379
三、编写redis常用工具类(网上很多,随便找一个就行)
@Component
public final class RedisUtil
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time)
try
if (time > 0)
redisTemplate.expire(key, time, TimeUnit.SECONDS);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key)
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key)
try
return redisTemplate.hasKey(key);
catch (Exception e)
e.printStackTrace();
return false;
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key)
if (key != null && key.length > 0)
if (key.length == 1)
redisTemplate.delete(key[0]);
else
redisTemplate.delete(CollectionUtils.arrayToList(key));
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key)
return key == null ? null : redisTemplate.opsForValue().get(key);
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value)
try
redisTemplate.opsForValue().set(key, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time)
try
if (time > 0)
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
else
set(key, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta)
if (delta < 0)
throw new RuntimeException("递增因子必须大于0");
return redisTemplate.opsForValue().increment(key, delta);
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta)
if (delta < 0)
throw new RuntimeException("递减因子必须大于0");
return redisTemplate.opsForValue().increment(key, -delta);
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item)
return redisTemplate.opsForHash().get(key, item);
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key)
return redisTemplate.opsForHash().entries(key);
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map)
try
redisTemplate.opsForHash().putAll(key, map);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time)
try
redisTemplate.opsForHash().putAll(key, map);
if (time > 0)
expire(key, time);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value)
try
redisTemplate.opsForHash().put(key, item, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time)
try
redisTemplate.opsForHash().put(key, item, value);
if (time > 0)
expire(key, time);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item)
redisTemplate.opsForHash().delete(key, item);
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item)
return redisTemplate.opsForHash().hasKey(key, item);
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by)
return redisTemplate.opsForHash().increment(key, item, by);
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by)
return redisTemplate.opsForHash().increment(key, item, -by);
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
*/
public Set<Object> sGet(String key)
try
return redisTemplate.opsForSet().members(key);
catch (Exception e)
e.printStackTrace();
return null;
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value)
try
return redisTemplate.opsForSet().isMember(key, value);
catch (Exception e)
e.printStackTrace();
return false;
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values)
try
return redisTemplate.opsForSet().add(key, values);
catch (Exception e)
e.printStackTrace();
return 0;
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values)
try
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
catch (Exception e)
e.printStackTrace();
return 0;
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key)
try
return redisTemplate.opsForSet().size(key);
catch (Exception e)
e.printStackTrace();
return 0;
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values)
try
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
catch (Exception e)
e.printStackTrace();
return 0;
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end)
try
return redisTemplate.opsForList().range(key, start, end);
catch (Exception e)
e.printStackTrace();
return null;
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key)
try
return redisTemplate.opsForList().size(key);
catch (Exception e)
e.printStackTrace();
return 0;
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index)
try
return redisTemplate.opsForList().index(key, index);
catch (Exception e)
e.printStackTrace();
return null;
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value)
try
redisTemplate.opsForList().rightPush(key, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time)
try
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value)
try
redisTemplate.opsForList().rightPushAll(key, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time)
try
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value)
try
redisTemplate.opsForList().set(key, index, value);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value)
try
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
catch (Exception e)
e.printStackTrace();
return 0;
四、redis用fastjson来序列化和反序列化
public class FastJsonJsonRedisSerializer<T> implements RedisSerializer<T>
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
public FastJsonJsonRedisSerializer(Class<T> clazz)
super();
this.clazz = clazz;
@Override
public byte[] serialize(T t) throws SerializationException
if (t == null)
return new byte[0];
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
@Override
public T deserialize(byte[] bytes) throws SerializationException
if (bytes == null || bytes.length <= 0)
return null;
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
protected JavaType getJavaType(Class<T> clazz)
return TypeFactory.defaultInstance().constructType(clazz);
五、编写redis配置类,并用fastjson来序列化和反序列化
@Configuration
public class RedisConfig
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory)
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
FastJsonJsonRedisSerializer jsonJsonRedisSerializer = new FastJsonJsonRedisSerializer<>(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
StringRedisSerializer serializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(serializer);
redisTemplate.setValueSerializer(jsonJsonRedisSerializer);
//hash的value也采用jackson序列化方式
redisTemplate.setHashKeySerializer(serializer);
redisTemplate.setHashValueSerializer(jsonJsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
六、连接测试
@Test
public void test3()
Long id = 4L;
SysUser user = userService.selectUserByUsername(id);
redisUtil.set(user.getUsername(),user);
SysUser u = (SysUser) redisUtil.get(user.getUsername());
System.out.println("从redis缓存中查询结果为:"+u);
springboot整合redis缓存一些知识点
前言
最近在做智能家居平台,考虑到家居的控制需要快速的响应于是打算使用redis缓存。一方面减少数据库压力另一方面又能提高响应速度。项目中使用的技术栈基本上都是大家熟悉的springboot全家桶,在springboot2.x以后操作redis的客户端推荐使用lettuce(生菜)取代jedis。
jedis的劣势主要在于直连redis,又无法做到弹性收缩。
一、配置文件
application.yml文件中的内容
spring: application: name: simple-lettuce cache: type: redis redis: # 缓存超时时间ms time-to-live: 60000 # 是否缓存空值 cache-null-values: true redis: host: 127.0.0.1 port: 6379 password: 123456 # 连接超时时间(毫秒) timeout: 60000 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0 database: 1 # spring2.x redis client 采用了lettuce(生菜),放弃使用jedis lettuce: # 关闭超时时间 shutdown-timeout: 30000 pool: # 连接池最大连接数(使用负值表示没有限制) 默认 8 max-active: 30 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 max-wait: -1 # 连接池中的最大空闲连接 默认 8 max-idle: 8 # 连接池中的最小空闲连接 默认 0 min-idle: 0
说明:
- spring.cache.type: redis
已经表明使用项目采用redis做为缓存方式。
- spring.cache.redis.cache-null-values: true
表示是否缓存空值,一般情况下是允许的。因为这涉及到缓存的三大问题:缓存穿透、缓存雪崩、缓存击穿。
如果设置false即不允许缓存空值,这样会导致很多请求数据库没有的数据时,不会缓存到redis导致每次都会请求到数据库。这种情况即:缓存穿透。
具体想初步了解这些概念可以参考文章:缓存三大问题及解决方案!
二、config配置类
@Configuration @EnableCaching public class RedisTemplateConfig extends CachingConfigurerSupport { private static Map<String, RedisCacheConfiguration> cacheMap = Maps.newHashMap(); @Bean(name = "stringRedisTemplate") @ConditionalOnMissingBean(name = "stringRedisTemplate") //表示:如果容器已经有redisTemplate bean就不再注入 public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory); } @Bean(name = "redisTemplate") @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { System.out.println("RedisTemplateConfig.RedisTemplate"); RedisTemplate<String, Object> template = new RedisTemplate<>(); // key的序列化采用StringRedisSerializer template.setKeySerializer(keySerializer()); template.setHashKeySerializer(keySerializer()); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(valueSerializer()); //使用fastjson序列化 template.setHashValueSerializer(valueSerializer()); //使用fastjson序列化 template.setConnectionFactory(lettuceConnectionFactory); return template; } /** * 添加自定义缓存异常处理 * 当缓存读写异常时,忽略异常 * 参考:https://blog.csdn.net/sz85850597/article/details/89301331 */ @Override public CacheErrorHandler errorHandler() { return new IgnoreCacheErrorHandler(); } @SuppressWarnings("Duplicates") @Bean @Primary//当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器 public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { // 默认配置 RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(keyPair()) .serializeValuesWith(valuePair()) .entryTtl(Duration.ofSeconds(DEFAULT_TTL_SECS)) //设置过期时间 .disableCachingNullValues(); // 其它配置 for(MyCaches cache : MyCaches.values()) { cacheMap.put(cache.name(), RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(keyPair()) .serializeValuesWith(valuePair()) .entryTtl(cache.getTtl()) // .disableCachingNullValues() // 表示不允许缓存空值 .disableKeyPrefix() // 不使用默认前缀 // .prefixKeysWith("mytest") // 添加自定义前缀 ); } /** 遍历MyCaches添加缓存配置*/ RedisCacheManager cacheManager = RedisCacheManager.builder( RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory) ) .cacheDefaults(defaultCacheConfig) .withInitialCacheConfigurations(cacheMap) .transactionAware() .build(); ParserConfig.getGlobalInstance().addAccept("mypackage.db.entity."); return cacheManager; } /** * key序列化方式 * @return */ private RedisSerializationContext.SerializationPair<String> keyPair() { RedisSerializationContext.SerializationPair<String> keyPair = RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()); return keyPair; } private RedisSerializer<String> keySerializer() { return new StringRedisSerializer(); } /** * value序列化方式 * @return */ private RedisSerializationContext.SerializationPair<Object> valuePair() { RedisSerializationContext.SerializationPair<Object> valuePair = RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()); return valuePair; } /** * 使用fastjson序列化 * @return */ private RedisSerializer<Object> valueSerializer() { MyFastJsonRedisSerializer<Object> fastJsonRedisSerializer = new MyFastJsonRedisSerializer<>(Object.class); return fastJsonRedisSerializer; } @Getter private enum MyCaches { defaultCache(Duration.ofDays(1)), MyCaches(Duration.ofMinutes(10)); MyCaches(Duration ttl) { this.ttl = ttl; } /** 失效时间 */ private Duration ttl = Duration.ofHours(1); } }
说明
1. 类上的注解@EnableCaching
表明开启缓存功能。
2. extends CachingConfigurerSupport
这个类就很丰富了,其实如果没有什么特别操作也可以不用继承这个类。
这个类可以支持动态选择缓存方式,比如项目中不止一种缓存方案,有可能有ehcache那么可以自定义在什么情况下使用redis使用情况下使用ehcache。还有一些有关异常的处理。我也不是很懂具体可以参考:
3. StringRedisTemplate和RedisTemplate的使用
三、缓存注解使用
@Cacheable 使用在查询方法上
@CachePut 使用在更新、保存方法上
@CacheEvict 使用在删除方法上
需要注意的是@Cacheable、@CachePut方法一定要有返回被缓存对象。因为注解使用的AOP切面如果没有返回值表示缓存对象为空值。
@CacheConfig注解在类上,可以选择使用哪个缓存、缓存管理器、Key生成器
好了以上就是最近在项目中的一些知识点总结,如果以后使用缓存有新的体会我会同步更新的。
以上是关于springboot整合redis做简单缓存的主要内容,如果未能解决你的问题,请参考以下文章
Springboot2.x+shiro+redis整合填坑 redis只做缓存的情况