1. 程式人生 > >SpringBoot 快取模組

SpringBoot 快取模組

預設的快取配置

在諸多的快取自動配置類中, SpringBoot預設裝配的是SimpleCacheConfigguration, 他使用的CacheManagerCurrentMapCacheManager, 使用 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());
}

刪除:

同樣,先刪除資料庫中的資料, 再刪除緩