Spring cache
基础概念

有CacheManager来管理缓存 缓存管理器中根据不同的业务来将不同的缓存进行分区
源码
可以查看CacheAutoConfiguration来查看spring cache自动配置了哪些东西


CacheProperties.class类中保存spring cache在yaml或properties中配置项

这里将不同的缓存类型添加到配置中
缓存类型如: redis 内存等
cacheType类如下

进入cacheConfiguration中 有块static语句块 根据不同

这里根据传入的缓存类型的不同 进行赋值

这里自动配置已经将缓存管理器创建好了
配置
首先导入pom包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
将cache类型设置为redis
spring:
cache:
type: redis
添加@EnableCaching 注解
@Cacheable
将该注解放入可缓存的业务方法 如: 查询商品分类的service方法中 若缓存中无数据则调用此方法并保存至redis中 若缓存中有数据则该方法不会被调用
默认缓存名称: cacacheName::SimpleKey []
默认缓存时间: 永久
默认存储类型: java反序列化
属性
value: 接受一个string类型的数组对象 缓存名称
key: 自定义缓存key名称 使用的是SpEL表达式 spring文档中有些

自定义缓存配置
首先查看源码进入到CacheAutoConfiguration 自动配置
配置中有个方法 这个方法是根据传入的缓存类型将缓存类型添加到配置中 我们跳转掉CacheConfigurations中

可以看到可支持的配置 我们使用的是redis 我们进入到RedisCacheConfiguration中

往下翻 这里有根据读取了默认的cache配置

我们点击RedisCacheConfiguation中 可以看到一些默认配

我们模仿它这个配置文件将cache value设置为json格式
序列化格式为RedisSerializer key我们就是用默认配置的
value格式我们进入RedisSerializer类ctrl + h查看子类

将包换json的序列化器写入即可 因为我使用的是springboot自带的jackson 所以我就用GenericJackson2JsonRedisSerializer
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
这样一来我们的配置文件的配置项就失效了
查看源码 这一堆if语句用于读取默认的配置文件 而我们因为四自定义缓存配置 所以无法走到这里
我们可以模仿它的写法 将cacheProperties注入 根据cacheProperties获取配置文件并设置

@Configuration
public class RedisCacheConfig {
@Autowired
private CacheProperties cacheProperties;
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
@CacheEvict
删除缓存数据
@CacheEvict(value = "category", key = "'findCategoryByFirstCategory'")
删除分类为category key为findCategoryByFirstCategory的缓存
若删除多个缓存有两种方法
方法一:
使用@Caching 将批量执行的方法写入数组中
@Caching(evict = {
@CacheEvict(value = "category", key = "'findCategoryByFirstCategory'"),
@CacheEvict(value = "category", key = "'getCatalogJson'"),
})
方法二:
删除分区中的所有数据
将category分区下的缓存清楚
@CacheEvict(value = "category", allEntries = true)
我们将相同类型的业务逻辑放一起比如分类数据 放在分类下
当数据发生修改时 将整个分类分区下的数据删除即可
避免缓存穿透
将空数据存储到缓存中 并设置一个默认时间 避免缓存击穿
spring:
cache:
type: redis
redis:
cache-null-values: true
缓存击穿解决
查询缓存时添加一个同步sync 添加本地锁 当请求进入时锁住 避免大量请求查询服务 只有当前服务可以去查询数据库 计算是当前服务有100个 100个服务去查数据库 对系统影响也不是很大
@Cacheable(value = {"category"}, key = "#root.methodName", sync = true)

浙公网安备 33010602011771号