默認的緩存配置
在諸多的緩存自動配置類中, SpringBoot默認裝配的是SimpleCacheConfigguration
, 他使用的CacheManager
是 CurrentMapCacheManager
, 使用 CurrentMap
當底層的數據結構,按照Cache的名字查詢出Cache, 每一個Cache中存在多個k-v鍵值對,緩存值
幾個主要的概念&常用緩存注解
名稱 | 解釋 |
---|---|
Cache | 緩存接口,主要實現由 RedisChache, EhCacheCachem , ConcurrentMapCache |
CacheManager | 緩存管理器,管理存放着不同類型的緩存 Cache 組件 |
@Cacheable | 加在方法上,根據方法的請求參數對結果進行緩存 |
@CacheEvict | 清空緩存 |
@CachePut | 保證方法被調用,又希望對方法的結果進行緩存 |
@EnableCaching | 添加在啟動類上,表示開始緩存 |
@keyGenerator | 緩存數據時key生成策略 |
serialize | 混存數據時,value的序列化策略 |
@Cacheable
上手使用
第一步:
開啟基於注解的緩存 @EnableCaching
第二步:
使用緩存注解@Cacheable
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
}
- value 和 cacheNames 作用一樣,都是在指定緩存的名字, 接收一個數組,可以指定多個緩存
- key, 指定當前這條數據在緩存中的唯一標識,支持SPEL表達式,默認是方法的參數值
最好都提前確定好使用哪個key
- keyGenerator, 指定key的生成策略
// 自定義key的生成器
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName() + "[" + Arrays.asList(objects).toString() + "]";
}
};
}
}
// 使用
@Cacheable(cacheNames = "dept",key = "#id",keyGenerator = "myKeyGenerator")
一般key和keyGenerator二選一就行
- cacheManager, 指定緩存管理器
- cacheResolver, 指定緩存解析器
- condition, 當條件為ture時, 進行緩存支持SPEL表達式
當id>0時,緩存
@Cacheable(cacheNames = "dept",key = "#id",condition = "#id>0")
使用and添加更多的條件
@Cacheable(cacheNames = "dept",key = "#id",condition = "#id>0 and #id<10")
- unless, 當條件為true時, 不進行緩存支持SPEL表達式
當結果為空時,不緩存
@Cacheable(cacheNames = "dept",key = "#id",unless="#result == null")
- sync, 異步緩存
異步模式下,不支持 unless
執行流程&時機
@Cacheable標注的方法在執行之前,會先去檢查緩存中有沒有這個數據, 根據這種對應關系查詢 key->value, key是使用keyGenerator生成的: 它的默認實現是SimpleKeyGenerate
參數個數 | key |
---|---|
沒有參數 | new SimpleKey() |
有一個參數 | 參數值 |
多個參數 | new SimpleKey(多個參數) |
常用的SPEL表達式
描述 | 示例 |
---|---|
當前被調用的方法名 | #root.mathodName |
當前被調用的方法 | #root.mathod |
當前被調用的目標對象 | #root.target |
當前被調用的目標對象類 | #root.targetClass |
當前被調用的方法的參數列表 | #root.args[0] 第一個參數, #root.args[1] 第二個參數... |
根據參數名字取出值 | #參數名, 也可以使用 #p0 #a0 0是參數的下標索引 |
當前方法的返回值 | #result |
@CachePut
調用時機:
目標方法執行完之后生效, @Cache被使用於修改操作比較多,哪怕緩存中已經存在目標值了,但是這個注解保證這個方法依然會執行,執行之后的結果被保存在緩存中
常用更新操作,前端會把id+實體傳遞到后端使用,我們就直接指定方法的返回值從新存進緩存時的key="#id"
, 如果前端只是給了實體,我們就使用key="#實體.id"
獲取key. 同時,他的執行時機是目標方法結束后執行, 所以也可以使用 key="#result.id"
, 拿出返回值的id
@CacheEvict 緩存清除
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
boolean allEntries() default false;
boolean beforeInvocation() default false;
}
- 同樣,key的默認值就是參數的id的值,也可以手動指定
- condition, 指定條件
- value鎖定Cache組件
- allEntries 指定是否刪除當前緩存組件中的全部值,默認是flase不全部刪除
- beforeInvocation, 緩存的清除,是否在方法之前執行, 默認是flase, 表示在方法執行之后執行
如果是在方法執行之前就清空緩存了, 然后方法執行過程中出現異常被中斷,緩存依然會被清除
@CacheEvict(value="",key="")
public void deleteById(Integer id){
// todo
}
@Caching
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
這是個組合注解,適用於更復雜的情況, 添加在方法上,使用:
@Caching(
cacheable={@Cacheable(...),@Cacheable(...) }
put={@CachePut(...),@CachePut(...) }
)
public void xxx(XXX){...}
@CacheConfig
使用場景, 比如,在一個部門的控制器中, 所有的緩存使用的value都是一樣的,所有的方法又不能不寫,於是使用@CacheConfig簡化開發
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
String[] cacheNames() default {};
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
}
添加在類頭上
- cacheNames , 指定緩存使用的公共的名字, 使用在標注在類頭上, 類中方法上的緩存相關的注解都可以不寫value="XXX"
整和其他混存中間件
整合Redis當緩存中間件
引入啟動器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
當我們添加進redis相關的啟動器之后, SpringBoot會使用RedisCacheConfigratioin
當做生效的自動配置類進行緩存相關的自動裝配,容器中使用的緩存管理器是
RedisCacheManager
, 這個緩存管理器創建的Cache為 RedisCache
, 進而操控redis進行數據的緩存
使用RedisTemplate,默認保存進去的數據 k-v 全是Obeject, 被保存的對象需要實現序列化接口, 雖然可以達到緩存的目的,但是對象被序列化成一堆看不懂的亂碼, 需要我們自定義Redis 的 Template, 以及自定義CacheManager, 但是這樣的話相對於它默認的配置就變的異常的麻煩;
使用redisTemplate做緩存的常用方式
查詢:
- 首先去redis中嘗試獲取,如果有redis中有值,直接返回redis中的結果 , 如果沒有,去數據庫中查詢,把結果存入redis
// todo 使用redis做緩存,減少和數據庫的接觸次數
public Label findById(Long labelId) {
// 先嘗試從緩存中查詢當前對象
Label label = (Label) redisTemplate.opsForValue().get("label_id" + labelId);
if (label==null){
// 從數據庫中查詢
// 將查出的結果存進緩存中
redisTemplate.opsForValue().set("label_id"+label.getId(),label);
}
return label;
修改:
先更新數據庫, 然后刪除redis中對應的緩存
public void update(Long labelId, Label label) {
label.setId(labelId);
Label save = labelRepository.save(label);
// todo 數據庫修改成功后, 將緩存刪除
redisTemplate.delete("label_id"+save.getId());
}
刪除:
同樣,先刪除數據庫中的數據, 再刪除緩存