小白教你完全理解 spring 緩存 @Cacheable、@CachePut、@CacheEvict、@Caching

概述

我們在開發的過程中可能會遇到一些請求比較頻繁。但是請求的結果有基本是不會變化的(或者是不會變化的)。

而我們每次都要經過運算或者是去數據庫中查找結果。這就回導致我們這個接口的效率十分的低下。

那對於這種接口我們就可以請求的時候先看下緩存中又沒有保存上一次的結果,如果有就直接返回。

沒有就如運算或者去數據庫中找。然後存入緩存中就可以了。這樣後面的請求就可以直接從緩存中得到結果了。效率自然就上去了。
示例圖

而 spring 的緩存註解就是爲我們提供了這些方法。

@Cacheable、@CachePut、@CacheEvict、@Caching。

下面我們來看下如何使用吧。

ps:本文所有的演示均以redis作爲緩存

@Cacheable 和 @CachePut

@Cacheable 表明spring在調用方法之前會先去緩存中查找又沒有之前這個方法的返回值。如果有就直接返回緩存,沒有則調用方法,然後將返回值放入緩存之中。

@CachePut Spring會將該方法的結果直接放入緩存之中。

兩者的區別

@Cacheable和@CachePut都是填充緩存唯一的不同就是@Cacheable會在調用方法之前檢查緩存,而@CachePut則不會。

@Cacheable和@CachePut有都有一些屬性。他們共有的屬性如下圖

屬性描述

屬性 類型 描述
value String[] 緩存的名稱
condition String SpEL表達式,如果是false則不會將緩存應用到方法調用上
key String SpEL表達式,緩存的KEY
unless String SpEL表達式,如果是false則不會將方法的返回值存儲到緩存上

舉例

假設我們目前要寫一個獲取用戶個人信息的接口,而這個人的個人信息又不是會經常變化。但是每次進入商城我們都要獲取一遍。如果不用緩存那麼我們每次都需要去數據庫查詢一遍。效率就不高。那我們就可以修改成緩存。

// userInfo 爲緩存的名稱 userId爲key,爲了區分不同人的信息
@Cacheable(value = "userInfo", key = "#userId")
    public User getUser(Long userId) {
        // 模擬數據庫查找
        User user=get(userId);
        return user;
    }

一下就是我們第一次調用這個方法,方法會把返回值存入緩存中。下一次請求就直接會調取緩存中的方法。而不會再去查詢。
在這裏插入圖片描述
我們再插入一條
在這裏插入圖片描述
而使用 @CachePut 則會直接調用方法然後將返回值直接更新的緩存中。比如我們的userInfo改變了,需要將新的緩存放入其中 ,那麼就可以使用以下代碼

  @CachePut(value = "userInfo", key = "#userId == -1")
    public User updateUserInfoCache(Long userId,Boolean) {
        // 模擬數據庫查找
        User user=get(userId);
        return user;
    }

會直接查詢出userInfo信息然後更新到緩存。

自定義緩存key

@Cacheable和@CachePut都帶有key屬性。key屬性可以使用任何SpEL表達式,但是大部分的時候我們會定義與值相關的,用於區分之後可以找到值,比如userInfo的Id

在爲編寫SpEL表達式的時候spring暴露出一些可以使用的元數據,如下表所示

表達式 描述
#root.args 傳遞給緩存的參數,形式爲數組
#root.caches 該方法執行時所對應的緩存,形式爲數組
#root.target 目標對象
#root.targetClass 目標對象的類,是 #root.target.class 的縮寫
#root.method 緩存方法
#root.methodName 緩存方法的名稱,是 #root.method.name 的縮寫
#result 方法調用的返回值(@Cacheable不可以使用)
#Argument Argument可替換成任意方法參數名(如#userId)或者參數索引(如 #a0、#p0)

我們下面來舉個例子來看下下面這些具體代表什麼。

    //  #root.args = [value,value2]
    //  #root.caches[0].getName = testCache
    //  #root.target = 當前對象 TestCache
    //  #root.targetClass = 當前對象類 TestCache
    //  #root.method = 當前方法 putCache
    //  #root.methodName = 當前方法名 putCache
    //  #result = 當前方法的返回值 resultInt
    //  #value(#Argument替換) = 方法參數value
    @CachePut(value = "testCache", key="#value")
    public Integer putCache(int value,int value2){
       int resultInt = value+1;
       return resultInt;
    }

這裏我們一般可以使用方法的參數作爲緩存的key。或者是返回值作爲key(如 #result.id )。

條件緩存

@Cacheable 和 @CachePut 的unlesscondition屬性可以實現條件化緩存。

如果unless屬性的SpEL的值返回結果爲true。那麼方法的返回值不會放到緩存中。
如果condition屬性的SpEL的值返回結果爲false,那麼方法緩存就會被禁用。

表面上看來兩者的作用是一樣的。但是其實是有一點差別的。

unless如果爲true,那麼會緩存方法的返回值不會放入緩存,但是依然會去緩存中查找又沒有匹配的值。如果有則會返回。
condition如果爲false,那麼緩存的方法返回值不會放入緩存,也不會去緩存中查找匹配的值。

下面我們用兩個例子來驗證以下

我們首先使用下面的方法將一個值放入緩存

    @CachePut(value = "testCache", key="#root.targetClass.getSimpleName()")
    public String putCache(){
        return "這是緩存的值";
    }

這時我們的緩存中會有一個值。
在這裏插入圖片描述

unless

    @Cacheable(value = "testCache", key="#root.targetClass.getSimpleName()", unless = "#value==10")
    public String putCacheUnless(int value){
        System.out.println("執行了方法");
        return "保存成功了";
    }

測試

System.out.println(testCache.putCacheUnless(10));

返回值如下圖,我們可以看出uless爲true,此時回去查找緩存中又沒有匹配的值,而我們緩存中正好有值,所以直接返回了。
在這裏插入圖片描述
如果我們把緩存刪除。然後再次調用,那麼結果如下。我們可以看到此時執行了方法。然後把方法的返回值返回了。
在這裏插入圖片描述

condition

    @Cacheable(value = "testCache", key="#root.targetClass.getSimpleName()", condition = "#value!=10")
    public String putCacheCondition(int value){
        System.out.println("執行了方法");
        return "保存成功了";
    }

測試

System.out.println(testCache.putCacheCondition(10));

此時結果如下。此時雖然緩存中有匹配的值,但是還是執行了方法,並返回了方法的返回值。
在這裏插入圖片描述
如果我們把緩存刪除了,結果如下,我們可以看到結果沒有發生變化可以看出condition不管緩存中又沒有匹配的值都不會去緩存中查找。
在這裏插入圖片描述

@CacheEvict

@CacheEvict 並不會往緩存中放入任何東西。而是會從緩存中刪除指定的緩存。一般當緩存不再有效的時候會調取此方法然後刪除。

屬性描述

屬性 類型 描述
value String[] 緩存的名稱
condition String SpEL表達式,如果是false則不會將緩存應用到方法調用上
key String SpEL表達式,緩存的KEY
allEntries boolean 如果爲true的話,無視key,會刪除當前緩存下的所有緩存
beforeInvocation boolean 如果爲true的話,會先刪除緩存再執行方法,如果爲false的話,會先執行方法再刪除緩存

結語

雖然緩存使用起來很方便,但是使用的時候要注意保證緩存的有效性。(在數據庫更新的時候要即使更新緩存)否則出現數據混亂問題。

以上就是我對spring的緩存註解的理解。如果有什麼不對的,歡迎大家指出,謝謝🙏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章