聲明:Spring緩存註解的使用非常簡單,主要是理解,所以本文主要以示例+註釋(圖片版)進行說明,核心部分
會給出文字版;當然本人測試時完整的項目代碼會放在GitHub上,鏈接見本文末。
目錄
背景簡述:
自Spring3.1開始,Spring就自帶了對緩存的支持。我們可以直接使用Spring緩存技術將某些數據放入本機的緩存中;Spring緩存技術也可以搭配其他緩存中間件(如Redis等)進行使用,將某些數據寫入到緩存中間件(緩存中間件可能在其他機器上)中。
啓用Spring緩存註解技術:
第一步:確認Spring版本不低於3.1。
注:本人用的是SpringBoot2.1.2.RELEASE,對應的Spring版本爲5.1.4.RELEASE。
第二步:在SpringBoot啓動類上啓用Spring緩存技術。
第三步:在類上或類中的方法上使用緩存註解。
注:這個【類】指的是注入了Spring容器中的。如果沒有注入,那麼在該類上或該類中的緩存註解是不會生效的。
Spring緩存註解總體介紹:
Spring提供的緩存註解有:
常用的註解有:@EnableCaching、@Cacheable、@CacheEvict、@CachePut、@Caching、@CacheConfig。
提示:本部分提到的key,可見本文下面關於緩存註解各屬性的介紹。
@EnableCaching:開關性註解,在項目啓動類或某個配置類上使用此註解後,則表示允許使用註解的方式進行緩存操作,如:
@Cacheable:可用於類或方法上;在目標方法執行前,會根據key先去緩存中查詢看是否有數據,有就直接
返回緩存中的key對應的value值。不再執行目標方法;無則執行目標方法,並將方法的返回值
作爲value,並以鍵值對的形式存入緩存,如:
@CachePut:可用於類或方法上;在執行完目標方法後,並將方法的返回值作爲value,並以鍵值對的形式存入緩存中,如:
@CacheEvict:可用於類或方法上;在執行完目標方法後,清除緩存中對應key的數據(如果緩存
中有對應key的數據緩存的話),如:
@Caching:此註解即可作爲@Cacheable、@CacheEvict、@CachePut三種註解中的的任何一種或幾種來使用,如:
@CacheConfig:@Cacheable、@CacheEvict、@CachePut這三個註解的cacheNames屬性是必
填項(或value屬性是必填項,因爲value屬性是cacheNames的別名屬性);如果上述
三種註解都用的是同一個cacheNames的話,那麼在每此都寫cacheNames的話,
就會顯得麻煩。如將@CacheConfig註解就是來配置一些公共屬性(如:cacheNames、
keyGenerator等)的值的,如:
緩存註解的常用屬性(以示例進行說明):
key:
key的來源可分爲三類,分別是:默認的、keyGenerator生成的、主動指定的。
下面在具體代碼中進行說明,注意閱讀註釋說明!
默認key:
keyGenerator生成key:
編寫配置類、定製化key生成器:
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
/**
* 定製化CachingConfigurer
*
* @author JustryDeng
* @date 2019/4/11 16:26
*/
@Configuration
public class MyCachingConfigurer extends CachingConfigurerSupport {
/**
* 定製化key生成器
*
* 設置 全限定類名 + 方法名 + 參數名 共同組成 key
*
* @return key生成器
* @date 2019/4/12 14:09
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (Object target, Method method, Object... params) -> {
StringBuilder sb = new StringBuilder(16);
sb.append(target.getClass().getName());
sb.append("_");
sb.append(method.getName());
sb.append("_");
for (int i = 0; i < params.length; i++) {
sb.append(params[i]);
if (i < params.length - 1) {
sb.append(",");
}
}
return sb.toString();
};
}
}
此時,若使用緩存註解時不指定key屬性,那麼就會默認採用Key生成器生成的註解:
主動指定key:
condition:
在激活註解功能前,進行condition驗證,如果condition結果爲true,則表明驗證通過,緩存註解生效;否則緩存註解不生效。
condition作用時機在:緩存註解檢查緩存中是否有對應的key-value 之前。
注:緩存註解檢查緩存中是否有對應的key-value 在 運行目標方法之前,
所以 condition作用時機也在運行目標方法之前。
實驗示例:
驗證:
cacheNames:
通過cacheNames對數據進行隔離,不同cacheName下可以有相同的key。也可稱呼cacheName爲命名空間。
下面驗證的是:當同時制定多個cacheName時,從哪一個cacheName取數據。
這裏先給出結論:
若屬性cacheNames(或屬性value)指定了多個命名空間;
-
當進行緩存存儲時,會在這些命名空間下都存一份key-value。
-
當進行緩存讀取時,會按照cacheNames值裏命名空間的順序,挨個挨個從命名空間中查找對應的key,如果在某個命名空間中查找打了對應的緩存,就不會再查找排在後面的命名空間,也不會再執行對應方法,直接返回緩存中的value值。
實驗示例:
驗證:
unless:
功能是:是否令註解(在方法執行後的功能)不生效;若unless的結果爲true,則(方法執行後的功能)不生效;若unless的結果爲false,則(方法執行後的)功能生效。
注:unless默認爲"",即相當於默認爲false。
unless的作用時機:目標方法運行後。
注:如果(因爲直接從緩存中獲取到了數據,而導致)目標方法沒有被執行,那麼unless字段不生效。
舉例說明一:
對於@Cacheable註解,在執行目標方法前,如果從緩存中查詢到了數據,那麼直接返回緩存中的數據;如果從 緩存中沒有查詢到數據,那麼執行目標方法,目標方法執行完畢之後,判斷unless的結果,若unless的結果爲true,那麼不緩存方法的返回值;若unless的結果爲false,那麼緩存方法的返回值。
舉例說明二:
對於@CachePut註解,在目標方法執行完畢之後,判斷unless的結果,若unless的結果爲true,那麼不緩存方法的返回值;若unless的結果爲false,那麼緩存方法的返回值。
注:因爲unless的作用時機是在方法運行完畢後,所以我們可以用SpEL表達式#result 來獲取方法的返回值。
實驗示例:
驗證:
說明:本人跑了幾次此測試方法,每次隨機產生的key(從代碼裏面可知,本人已入參參數爲key)都是之前緩存
裏面沒有的,也就是說每次都會執行目標方法;發現大於等於5000的隨機數都存入緩存彙總了;而小
於5000的隨機數則沒有。
allEntries:
此屬性主要出現在@CacheEvict註解中,表示是否清除指定命名空間中的所有數據,默認爲false。
beforeInvocation:
此屬性主要出現在@CacheEvict註解中,表示 是否在目標方法執行前使 此註解生效。 默認爲false,即:目標方法執行完畢後此註解生效。
緩存註解使用在返回值爲viod方法上的測試:
結論是:緩存註解作用於void方法上,仍然會向緩存中進行存儲,不過鍵值對中的value爲null。
實驗示例:
驗證:
筆者寄語:
關於Spring緩存註解的其他一些屬性、用法等這裏就不再一一敘述了,感興趣的可自行查詢相關資料或閱讀源碼進行測試。