- 前言
- 源码
- 入门
- springBoot 自动配置缓冲中间件。
- 介绍几个注解
- 整合SpringCache
- 引入依赖
- 开启缓存
- 配置属性
- 了解注解
- 录入测试文本
- 测试案例分析
- 可能遇到的问题,击穿 雪崩 穿透
- 解决雪崩
- 解决穿透
- 自定义缓存
- 总结
对于一些后端系统,如果不是电商系统,在前文中的redis集成之后,一般会采用使用redis 封装工具类进行存储一些数据,但是在springBoot前 spring已经集成了缓存中间件,一般来说普通的缓存可以使用缓存中间件进行使用,比如说配置列表,比如说一些固定的json,库存配置,地址配置,状态机配置,对于一般不怎么改动的且并发量在100以内的使用注解配置要舒服的多,今天还是看了一下源码顺便把这个好用的缓存件总结下来。
spring 从3.1就开始定义了Cache 和CaheManager 接口来实现工程上面的缓存技术并支持JCache注解来简化开发,具体内容可以参考spring的官网文档。Cache接口为缓存组件规范了定义,包含缓存的各种操作集合;
源码 入门关于缓存管理器接口,提供了 获取缓存,和通过名字获取缓存
缓存件里面提供了多种缓存中间件,有普通的也有组件化以及redis等缓存管理器
通过当前map来管理不同名字的缓存
在赋予缓存名字的时候会根据是否存在命名,如果没有会创建当前map缓存方法
由上文源码可以了解到
缓存 — 缓存管理器 – 缓存
springBoot 自动配置缓冲中间件。
通过注入缓存的搜索器去搜索各种缓存配置
通过mapping来映射缓存类型配置
缓存类型配置由map集合收纳
缓存类型枚举
文中以redis为例 通过映射redis类型获取 redis 缓存配置 通过源码可以看到构造函数 存在redisCacheConfiguration如果不存在默认配置,通过bean帮我们创建好了 CacheManager 管理器
从CacheProperties Application写入的Spring.Cache里面注入过来的缓存名称。
Spring.Cache 配置
如果没有缓存名字会自己初始化一个缓存名称
拿到管理器之后确定配置 获取管理器配置
下面关于如果没有redisCacheConfigration那么默认创建可以看到key 是String 序列号,值是JDK序列号,如果后期脚本使用那么存储在redis缓存的值需要进行再处理为json
从文中可以看到,缓存管理器以及缓存只要开启缓存,并配置简单的缓存类型,那么缓存中间件就可以自动化为我们配置好所需要的所有管理器,以及缓存
SpringBoot已经启用自动化配置进行缓存封装,这种情况,以常用的redis 为例进行缓冲示例
介绍几个注解- @Cacheable 触发保存到缓存 常用
- @CacheEvict 触发将缓冲删除
- @CachePut 不影响方法执行更新
- @Cacheing 组合多个操作
- @CacheConfig 在类级别共享缓冲配置
开启缓存 配置属性org.springframework.boot spring-boot-starter-cache org.springframework.boot spring-boot-starter-redis 1.3.2.RELEASE
spring.cache.type=redis spring.cache.redis.time-to-live=6000 # 防止穿透 spring.cache.redis.cache-null-values=true # 指定前缀 就用指定的前缀,如果没有默认使用缓存名字作为前缀 spring.cache.redis.key-prefix=cache_ spring.cache.redis.use-key-prefix=true了解注解 录入测试文本 测试案例分析
@Cacheable 代表当前结果需要缓存 如果缓存中有 方法不需要调用 ,如果缓存没有查询结果映射到redis缓存中去
@AliasFor("cacheNames")
String[] value() default {};
可能遇到的问题,击穿 雪崩 穿透
每个需要缓存的数据 指定业务类型 放到缓存分区管理器中,方便后期从redis 获取
缓存分区 CacheNames 缓存建 key = "#root.methodName"方法名
配置文件 -> spring.cache.redis.key-prefix=cache_ 缓存前缀 cache_
spring.cache.redis.time-to-live=6000 过期时间 雪崩设置过期时间 6000 毫秒
解决穿透spring.cache.redis.cache-null-values=true 穿透是否存储null 解决
击穿 设置 注解 sync = true 过期同步线程获取
@Cacheable(value = {"location"},key = "#root.methodName",sync = true)
前面源码说到, 由于存储值进行的默认是JDK序列, 如果没有RedisCacheConfiguration 才会创建
捋一下思路
CacheAutoConfiguration -> getConfigurationClass -> RedisCacheConfiguration ->cacheManager -> RedisCacheManager ->determineConfiguration -> 判断是否为空->
由于缓存的redis 过期时间为空 且 序列化方式为JDK 序列存储,所以建议定义存储 为 json 方便后面使用
源码构造配置
// 判断是否为空 -> config.serializevaluesWith(SerializationPair .fromSerializer(new JdkSerializationRedisSerializer(classLoader))); RedisCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker, ObjectProviderredisCacheConfiguration) { this.cacheProperties = cacheProperties; this.customizerInvoker = customizerInvoker; this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable(); }
改造
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
public class MyCacheConfig {
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
config.serializeKeysWith( RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
总结
读多写少 及时性 一致性要求不高的数据, 完全可以使用Srping - Cache 特殊情况需要强及时性及数据一致性,可以进行特殊设计



