<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
在yml下
spring:
cache:
type: redis
redis:
key-prefix: Client_ #key前缀,如果配置了cacheManager则失效
use-key-prefix: true #是否开启key前缀
cache-null-values: true #是否缓存空值
在配置类上加上@EnableCaching开启缓存
配置序列化器
RedisConfig implements CachingConfigurer
redis的序列化器配置见前文,
/**
* 对缓存进行序列化和反序列化的配置
* @param factory redis连接工厂
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory){
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1)) // 配置缓存时长
// 注意0为永久用于 Duration.ZERO 声明永久缓存。
.prefixCacheNameWith("Client:") // 前缀
.computePrefixWith(cacheName -> "caching:" + cacheName); //前缀动态
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
在这里配置就差不多了,在需要进行缓存操作的地方加注解就可以了
该注解为缓存获取注解:
如果缓存中没有则进行查询在添加到缓存,如果缓存有则直接走缓存
属性
SpEL 表达式:
- 可以使用 #root.method, #root.target, and #root.caches 来指定 方法, 目标对象和受影响的缓存的method引用
@cacheable(value =“phone”,key="#root.methodName”)
指定方法名来作为- 可以使用#形参名字来使用,下面是用phone的id来
@Cacheable(value = “Phone”,key=“#phone.id”)
public ResultgetAll(Phone phone)
- 方法参数可以通过索引访问。例如,可以通过 或 #p1 #a1访问#root.args[1]第二个参数。如果该信息可用,也可以按名称访问参数。
@Component
public class MyGenerator implements KeyGenerator{
@override
public Object generate(object o, Method method, object... objects){
}
}
更新缓存
每次都会调用方法,把返回值缓存起来,每次都会更新/新增
注解和前面一样
删除缓存
调用后删除指定缓存
注解相同,多了几个
我是2种方法都配置了的
主要在与对于每一个cacheName设置不同的RedisCacheConfiguration
多个cacheName就加多个withCacheConfiguration就可以了
/**
* 对缓存进行序列化和反序列化的配置
*
* @param factory redis连接工厂
* @return
*/
@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1)) // 配置缓存时长
.prefixCacheNameWith("Client:") // 前缀
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
// 如果不需要第二种就把CustomRedisCacheManager换成RedisCacheManager
return CustomRedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(factory)
.cacheDefaults(config)
.withCacheConfiguration("contact:relation", getCacheConfigurationWithTtl(redisTemplate, Duration.ofMinutes(30)))
.transactionAware()
.build();
}
RedisCacheConfiguration getCacheConfigurationWithTtl(RedisTemplate<String, Object> template, Duration time) {
return RedisCacheConfiguration
.defaultCacheConfig()
.prefixCacheNameWith("Client:") // 前缀
// 设置key为String
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
// 设置value 为自动转Json的Object
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
// 不缓存null
.disableCachingNullValues()
// 缓存数据保存1小时
.entryTtl(time);
}
在cacheName上面加上#时间就可以了
/**
* 自定义缓存管理器
*/
public class CustomRedisCacheManager extends RedisCacheManager {
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
/**
* 针对@Cacheable设置缓存过期时间
* @param name
* @param cacheConfig
* @return
*/
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
String[] array = StringUtils.delimitedListToStringArray(name, "#");
// 解析TTL
if (array.length > 2) {
char c = array[1].charAt(array.length - 1);
long ttl = Long.parseLong(StrUtil.sub(array[1], 0, array[1].length() - 1));
cacheConfig = switch (c){
case 's','S' -> cacheConfig.entryTtl(Duration.ofSeconds(ttl));
case 'm','M' -> cacheConfig.entryTtl(Duration.ofMinutes(ttl));
case 'h','H' -> cacheConfig.entryTtl(Duration.ofHours(ttl));
case 'd','D' -> cacheConfig.entryTtl(Duration.ofDays(ttl));
default -> cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(array[1])));
}
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl)); // 注意单位我此处用的是秒,而非毫秒
}
return super.createRedisCache(array[0], cacheConfig);
}
}
redis如果连接不上,springboot执行报错,从而导致正常业务造成错误。
所以我们需要重写异常处理的方法
重写继承CachingConfigurer的errorHandler方法,直接打印就可以了。
/**
* 配置当redis连接不上时被缓存注解标注的方法绕过Redis
*/
@Bean
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
log.error("redis异常:key=[{}]", key, exception);
}
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
log.error("redis异常:key=[{}]", key, exception);
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
log.error("redis异常:key=[{}]", key, exception);
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
log.error("redis异常:", exception);
}
};
}
缩小redis的读取超时timeout时间。
但是我发现需要2个timeout时间才会执行正常的逻辑,所以我选择700ms
redis:
database: 5
host: localhost
port: 6379
timeout: 700ms