SpringBoot Cache キャッシュ
皆さんこんにちは、私はそれについて考えています。
最近、SpringBoot 関連のソースコードをレビューしています。より洗練された解決策が存在する可能性のある以前の問題が多数見つかりました。
キャッシュキャッシュと同じように、そう思う人は多くないでしょう。Mybatis を統合すると、Mybatis の 2 次キャッシュのみが動作できるようになりますか? または、Redis などのキャッシュ データベースにアクセスするだけです。
実際、SpringBoot の SimpleCache もメモリベースのキャッシュ テクノロジであり、少量のデータをキャッシュするのに適しています。
- 頻繁にクエリされるデータをバッファリングする
- 計算コストのかかるデータのバッファリング
- ユーザーの訪問数を記録して、インターフェースのフロー制御のフローを制限します。
- 重複した送信を防ぐために、フォームの送信ステータスを記録します。
SpringBoot Cache について十分に理解する前は、Map をカプセル化して自分でキャッシュするかもしれません。
しかし、SpringBoot Cache が非常に多くのキャッシュをサポートしていることがわかりました。
SpringBootキャッシュのロード順序
JCache や Redis などの依存関係を導入しない場合、SpringBoot はデフォルトで SimpleCache をキャッシュとして使用し、その基礎となる層はキャッシュされたコンテンツを保存するコンテナとして ConcurrentMap を使用し、完全な API を備えています。
SimpleCache プロパティ
そうそう~
道教だけでは十分ではありません、学ばなければなりません~
テキストを入力してください
まず SpringBoot を使用して初期化し、テーブル CRUD を実装する SSM プロジェクトを作成します。これは重要ではありません。
SpringBoot スタートアップ クラスにアノテーションを追加し @EnableCaching
、キャッシュを有効にする
ブレークポイントをインターセプトすることで、SpringBoot 自動構成クラスを見つけることができます org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
(この手順は Spring 自動構成クラスに関連しています。見つけ方がわからない場合は、「Spring 自動構成クラス」関連情報を読むか、「Spring 自動構成クラス」を参照してください) )
SpringBootキャッシュのロード順序
CacheAutoConfiguration
導入すると 、 CacheConfigurationImportSelector.class
CacheAutoConfiguration.java
CacheConfigurationImportSelector
是 CacheAutoConfiguration
中第一个内部类,它实现了 ImportSelector
重写了 selectImports
方法,那就是要重新的自动配置一下
我们就可以找到文章开头的那张图,在不引入其他 cache 的情况下,默认采用 SimpleCache 的放松
springboot-cache 加载顺序
SimpleCacheConfiguration
里面只有一个 @Bean 注解,就是返回一个 ConcurrentMapCacheManager
而 ConcurrentMapCacheManager
里面就有我们之前看到的
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
还有一个最重要的方法, Cache getCache(String name)
,值得一提的是 ConcurrentMapCacheManager
并不是 SpringBoot 中的源码,而是 Spring-context 中的。
Cacheable 运行流程
简单看一下,就是先到 cacheMap (ConcurrentMap) 中获取一下。没获取到就进入业务逻辑进行查询
目标代码
文字描述一下运行流程
- 方法运行之前,先去查询 Cache 缓存组件,安装 CacheName 指定的名字获取,第一次获取缓存没有 Cache 组件时,就去创建
createConcurrentMapCache(name)
。 - 去 Cache 中查找缓存内容,使用 key 默认就是方法参数,如果是多个参数,总是会根据一种策略生成一种 key
- 没有查到缓存就调用目标方法
- 然后将目标方法结果放入缓存中
一句话总结: @Cacheable 标注的方法执行前会查询换成中是否有这个数据,默认按照参数作为key进行查询,如果没有调用目标方法将结果放入缓存,以后在调用时,直接使用缓存中数据。
除了 @Cacheable 注解之外呢,还有其他两种常用注解
@CachePut(Update)
即调用方法,又更新缓存,一般用于更新操作。
运行流程:
- 先调用目标方法
- 将目标方法结果缓存起来
CachePut 案例
@CacheEvict(Delete)
清除缓存,需要指定名字和key
属性:
- value / cacheNames 缓存名字
- key 缓存键
- allEntrles 是否清除这个缓存 value 中的所有 key,如果为 true 则清除所有 key(与 key 属性二选一)
- beforelnvocation 默认 false,执行方法后再清除缓存,如果设置为 true 则执行目标方法前清除缓存 作用:可能目标方法存在异常 实际数据清除了,但因为别的事务导致异常,方法执行失败,缓存就没有删除,(数据库已删除,缓存未删除)
CacheEvict 案例
基于 Redis 的缓存
第一步启动一个 Redis 然后项目中添加 redis 依赖
启动 Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
通过断点老办法,我们可以找到 RedisAutoConfiguration
RedisAutoConfiguration
熟悉的感觉来了,一个 redisTemplate
还有一个 stringRedisTemplate
其实 stringRedisTemplate
就是继承了 redisTemplate
SimpleRedisTemplate 继承 RedisTemplate
简单的介绍了一下 SpringBoot 自动配置 Redis 相关的源码之后,迅速的写一个例子吧
缓存顺利的加载到 Redis 中了
没有 Redis 之前使用的 SimpleCache ,使用了 仅仅是因为加入了 Reids 的依赖,缓存就顺利的写到了 Redis 里,这是因为 SpringBoot 自动配置顺序有关:
我们可以看到 RedisCacheConfiguration
高于 SimpleCacheConfiguration
的
难道是 SpringBoot 一个一个扫描,发现一个之后就 continue 吗?当然不是了,SpringBoot 就玩的很优雅
我们打开原先的 SimpleCacheConfiguration
可以看到注解上有这样一段代码
@ConditionalOnMissingBean(CacheManager.class)
@Conditional
的意思是如果存在或者 (类、注解...) 的情况下,就加载此配置类,这样是 SpringBoot 自动化配置的一个核心点!
那 @ConditionalOnMissingBean
的意思是,如果存在这个类,就不让这个自动配置类生效。我们再根据上图的加载优先顺序,每个配置类中,都有这样一句话。也都有一个 cacheManager 的 Bean
@Bean
ConcurrentMapCacheManager cacheManager()...
これは、 CacheManager
後でロードされるキャッシュ構成クラスを作成した人はインスタンス化できないことを意味します。
とても独創的なデザインですね。このアイデアは SpringBoot の他の場所にも反映されています。
SpringBoot がオートロードによって CacheManager を作成する方法はすでにわかったので、もちろん、同じ方法で CacheManager をカスタマイズすることもできます。
Redis のデフォルト設定では、JDK に付属するシリアル化を使用して value 値をキャッシュします。この設定も同じ方法で変更できます。
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial =
new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 定制缓存数据序列化方式及时效
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// 设置有效期为1天
.entryTtl(Duration.ofDays(1))
// 设置 key 的序列化方式为 String
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(strSerializer))
// 设置 value 的序列化方式为 Json
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(jacksonSeial)) .disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager .builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}
効果をもう一度見てみましょう!
読んでくれてありがとう!