首頁 > 易卦

SpringBoot的快取實現Cache和自定義快取管理器

作者:由 java小資 發表于 易卦日期:2022-12-18

快取一定要使用註解嗎

SpringBoot的快取實現Cache和自定義快取管理器

專案中總會有些資料是訪問頻繁,但是這些資料有基本上不會修改,比如,商品的分類這些的資料,其基本上不會修改,但在處理這些資料的時候往往還需要處理成樹結構等格式,也就多了許多沒有意義的消耗。下面我們來看下spring給我帶來的快取技術Caching。並在結尾會使用redis替換快取管理器,並實現自定義的快取管理器。

基本的上使用就是兩步:

1、開啟基於註解的快取

在啟動類或者快取的配置類上新增@EnableCaching

2快取的註解使用

@Cacheable @CacheEvict @CachePut @Caching @CacheConfig

原理:

在SpringBoot專案啟動時,自定配置類會載入CacheAutoConfiguration的自動配置類。該類會在容器中自動注入以下幾個配置類,根據不用的條件來決定是哪個配置類起作用。

SpringBoot的快取實現Cache和自定義快取管理器

透過執行程式碼發現其預設的快取的配置類是SimpleCacheConfiguration,其給容器中註冊了一個CacheManager:ConcurrentMapCacheManager,最後將資料儲存在ConcurrentMap中

SpringBoot的快取實現Cache和自定義快取管理器

SpringBoot的快取實現Cache和自定義快取管理器

註解的具體使用

SpringBoot的快取實現Cache和自定義快取管理器

官網註解截圖

@Cacheable:

執行流程

1、方法執行之前,先去查詢Cache(快取元件),按照cacheNames指定的名字獲取;(CacheManager先獲取相應的快取),第一次獲取快取如果沒有Cache元件會自動建立。

2、去Cache中查詢快取的內容,使用一個key,預設就是方法的引數;key是按照某種策略生成的;預設是使用keyGenerator生成的,預設使用SimpleKeyGenerator生成key;SimpleKeyGenerator生成key的預設策略;

如果沒有引數;key=new SimpleKey();

如果有一個引數:key=引數的值

如果有多個引數:key=new SimpleKey(params);

3、沒有查到快取就呼叫目標方法;

4、將目標方法返回的結果,放進快取中

@Cacheable標註的方法執行之前先來檢查快取中有沒有這個資料,預設按照引數的值作為key去查詢快取,如果沒有就執行方法並將結果放入快取;以後再來呼叫就可以直接使用快取中的資料;

核心:

1、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】元件

2、key使用keyGenerator生成的,預設是SimpleKeyGenerator

具體引數:

cacheNames/value

:指定快取元件的名字;將方法的返回結果放在哪個快取中,是陣列的方式,可以指定多個快取;

key

:快取資料使用的key;可以用它來指定。預設是使用方法引數的值 1-方法的返回值

編寫SpEL; #i d;引數id的值 #a0 #p0 #root。args[0] getEmp[2]

keyGenerator

:key的生成器;可以自己指定key的生成器的元件id

key/keyGenerator二選一使用;

cacheManager

:指定快取管理器;或者cacheResolver指定獲取解析器 兩者二選一使用

condition

:指定符合條件的情況下才快取;,condition = “#id>0” condition = “#a0>1”:第一個引數的值>1的時候才進行快取

unless

:否定快取;當unless指定的條件為true,方法的返回值就不會被快取;可以獲取到結果進行判斷unless = “#result == null” unless = “#a0==2”:如果第一個引數的值是2,結果不快取;

sync

:是否使用非同步模式

下圖為el表示式的寫法

SpringBoot的快取實現Cache和自定義快取管理器

SpringBoot的快取實現Cache和自定義快取管理器

@CachePut:

既呼叫方法,又更新快取資料;同步更新快取,修改了資料庫的某個資料,同時更新快取;

執行流程:

1、先呼叫目標方法

2、將目標方法的結果快取起來

需要注意的是此處的key屬性要個查詢的key一直,要不最後修改的是一份快取,查詢的是一套快取,從而造成資料的不一致。

@CacheEvict:快取清除

具體引數:

key:

指定要清除的資料

allEntries

= true:指定清除這個快取中所有的資料

beforeInvocation

= false:快取的清除是否在方法之前執行

預設代表快取清除操作是在方法執行之後執行;如果出現異常快取就不會清除

beforeInvocation = true: 代表清除快取操作是在方法執行之前執行,無論方法是否出現異常,快取都清除

@Caching

定義複雜的快取規則

@CacheConfig抽取快取的公共配置

@CacheConfig(cacheNames=“emp”/*,cacheManager = “employeeCacheManager”*/) //抽取快取的公共配置

cacheNames 指定該類全域性的快取名稱

keyGenerator 指定key的生產策略

cacheManager 指定快取管理器

重點:使用Redis替換預設的快取管理器

新增pom依賴

org。springframework。boot

spring-boot-starter-data-redis

redis。clients

jedis

自定義快取管理器(springboot2版本使用,1版本的自己查詢下就可以了 那個比較簡單)

一定要注意 儲存的資料實體類一定以實現序列號介面

Serializable

@Bean

public

RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

// 使用快取的預設配置

RedisCacheConfiguration config = RedisCacheConfiguration。defaultCacheConfig();

// 使用 GenericJackson2JsonRedisSerializer 作為序列化器

config = config。serializeValuesWith(

RedisSerializationContext。SerializationPair。fromSerializer(

new

GenericJackson2JsonRedisSerializer()));

RedisCacheManager。RedisCacheManagerBuilder builder =

RedisCacheManager。builder(redisConnectionFactory)。cacheDefaults(config);

return

builder。build();

}

上面為什麼會選用GenericJackson2JsonRedisSerializer 作為序列化器呢,以下說下我的踩坑史。JdkSerializationRedisSerializer是預設序列化方式,是最簡單的也是最安全的,只要實現了Serializer介面,實體型別,集合,Map等都能序列化與反序列化,但缺陷是序列化資料很多,會對redis造成更大壓力,且可讀性和跨平臺基本無法實現Jackson2JsonRedisSerializer用的是json的序列化方式,能解決JdkSerializationRedisSerializer帶來的缺陷,但複雜型別(集合,泛型,實體包裝類)反序列化時會報錯,且Jackson2JsonRedisSerializer需要指明序列化的類Class,這代表一個實體類就有一個RedisCacheManager,程式碼冗餘。

最後使用GenericJackson2JsonRedisSerializer,此類不用需要指明序列化的類,寫一個RedisCacheManager即可,程式碼更精簡,複雜型別(集合,泛型)反序列化時不會報錯,檢視可以發現實現原理是在json資料中放一個@class屬性,指定了類的全路徑包名,方便反序列化,以上為最後選擇GenericJackson2JsonRedisSerializer的原因。