配置Redis的序列化是为了解决程序中的数据转化成Redis二进制数据后无法直观查看数据信息的问题。
spring-boot-starter-data-redis默认会使用lettuce,本次使用的是Jedis,所以依赖中我们要屏蔽掉lettuce而改用jedis。
Springboot会帮我们自动装配Redis,自动生成RedisConnectionFactory、RedisTemplate、StringRedisTemplate等常用的Redis对象。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.7.5version>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
在spring-data-redis中,RedisConnection通过RedisConnectionFactory创建,Spring为了简化以上操作,做出了简化,并诞生了RedisTemplate
因为Redis的数据以二进制的方式存储,与Java数据不相同,所以直接存入Java数据到Redis中会导致你在使用RedisCli查看数据时,是我们无法看懂的二进制序列化数据,为了解决这个问题,我们需要对Redis配置序列化的方式。
package com.example.demo.config.redis;
/**
* @Author : HuangJiajian
* @create 2022/10/24 9:07
*/
@Configuration
public class RedisConfig {
/**
* @Author HJJ
* @Date 2022-12-30 15:49
* @Params
* @Return
* @Description 注入CacheManager
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory)
.cacheDefaults(defaultCacheConfig())
.transactionAware()
.build();
}
/**
* @Author HJJ
* @Date 2022-12-30 15:48
* @Params
* @Return
* @Description 配置通过@Cacheable注解缓存的序列化方式以及过期时间
*/
private RedisCacheConfiguration defaultCacheConfig() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
serializer.setObjectMapper(mapper);
return RedisCacheConfiguration.defaultCacheConfig()
// 20秒过期
.entryTtl(Duration.ofSeconds(20))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
.disableCachingNullValues();
}
}
在Controller层中,使用@Cacheable注解来缓存return的数据。
@Cacheable(value, key)中,value是属性名称,key是标识,在RedisCli中应该通过
get value::key
的形式读取数据,如get User::Jack
、get User::John
package com.example.demo.controller;
/**
* @Author : HuangJiajian
* @create 2022/12/2 10:59
*/
@RestController
@RequestMapping("/cache")
public class TestRedis {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/str")
@Cacheable(key = "#str", value = "Str")
public String cacheStr(String str) {
System.out.println("缓存str(第二次同参数时,不应该出现这一打印信息):" + str);
return cache;
}
@GetMapping("/user")
@Cacheable(key = "#username", value = "User")
public AjaxResult cacheUser(String username) {
UserEntity userEntity = new UserEntity(u, "123456");
System.out.println("缓存username(第二次同参数时,不应该出现这一打印信息):" + username);
return new AjaxResult(AjaxResult.CODE.SUCCESS, "success", userEntity);
}
}
验证缓存
发送发送2次GET请求/cache/str
,参数都是?str=value1
,结果如下:
第一次请求:
控制台打印出”缓存str(第二次同参数时,不应该出现这一打印信息):value1“
浏览器获取到数据“value1”
RedisCli运行
get Str::value1
打印出"value1"第二次请求:
控制台不打印
浏览器获取到数据“value1”
RedisCli运行
get Str::value1
打印出"value1"
事务处理简单来讲就是“要么是0,要么是1”。常见于电商系统,如商品在用户提交的订单后库存便减少,但若用户取消支付或支付失败,就需要将库存数据回滚。Redis也具有部分事务处理的功能。
Redis的事务处理包含三个部分:watch(监控)…multi(开始)…exec(执行)
@GetMapping("/multi")
public String testMulti(){
stringRedisTemplate.opsForValue().set("key1", "value1");
List list = (List) stringRedisTemplate.execute(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations ops) throws DataAccessException {
ops.watch("key1");
ops.multi();
ops.opsForValue().set("key2", "value2");
// 还未执行事务,所以数据是空的
System.out.println(ops.opsForValue().get("key2")); // null
// 情况1:在multi和exec之间打断点,然后使用reids-cli修改key1的值,exec之后Redis会监听到key1已经发生改变,故会回滚事务。
// 情况2:key1增操作错误,但Redis仅存在队列中,直到exec时才会报错,应避免
// ops.opsForValue().increment("key1", 1);
ops.exec();
return null;
}
});
System.out.println(list);
return "end";
}
@Cacheable的原理是SpringAOP,很多@Cacheable失效的原因都是因为RedisConfig的初始化在其使用位置之后,所以无法执行AOP拦截,导致@Cacheable注解失效。这个可以通过使用@Autowired StringRedisTemplate stringRedisTemplate;
实现Redis缓存,但这样并不会用上RedisConfig中的配置,所以需要在函数中自己配置序列化方式和过期时间等。
还有一种是@Cacheable方法A(),然后使用方法B()去调用方法A,我知道你想要让B调用A时,返回A在Redis中的缓存数据,但这样也是无法做到的。