快取抽象層Spring cache實戰操作
Spring快取抽象
Spring從3.1開始定義了一系列抽象介面來統一不同的快取技術;並支援使用JCache(JSR-107)註解簡化我們進行快取開發。Spring Cache 只負責維護抽象層,具體的實現由你的技術選型來決定。將快取處理和快取技術解除耦合。
依賴引入
Spring cache 抽象由spring-context相關元件實現。非Spring Boot 專案可通過引入該模組進行整合。
Spring Boot 專案可引入以下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
同時可能需要引入你採用的快取中介軟體客戶端;比如 Ehcache、redis等。
兩個重要抽象概念
- Cache 快取抽象規範介面,定義快取一些了操作。實現有:RedisCache、EhCacheCache、ConcurrentMapCache等
- CacheManager 快取管理器,管理Cache的生命週期。
常用的一些註解
Spring cache 提供了一系列的註解,將我們從程式設計開發中解放出來。讓我們更加關注於業務開發。
@EnableCaching
該註解是啟用Spring cache 的開關。必須開啟才能使用Spring cache相關功能。
@Cacheable
可以標記在一個方法或者類上。方法級只針對該方法。類上則針對類內所有的方法。對於一個支援快取的方法,Spring會在其被呼叫後將其返回值快取起來,以保證下次利用同樣的引數來執行該方法時可以直接從快取中獲取結果,而不需要再次執行該方法。Spring在快取方法的返回值時是以鍵值對進行快取的,值就是方法的返回結果。
public @interface Cacheable { @AliasFor("cacheNames") String[] value() default {}; //和value註解差不多,二選一 String[] cacheNames() default {}; // 該次快取的key String key() default ""; //key的生成器。key/keyGenerator二選一使用 String keyGenerator() default ""; //指定快取管理器 一般使用預設 String cacheManager() default ""; //或者指定獲取解析器 一般使用預設 String cacheResolver() default ""; //條件符合則快取 使用的比較多 支援SpEL String condition() default ""; //條件符合則不快取 使用的比較多 支援SpEL String unless() default ""; //是否使用非同步模式 boolean sync() default false; }
後面有個別註解屬性跟這個基本相同不進行重複介紹。
@CacheConfig
作用於快取介面上,來對該介面下的一些重複配置(快取名稱、key生成器、快取管理器、快取處理器)進行歸納處理。其他屬性可參考Cacheable。
@CachePut
該註解容易跟@Cacheable混淆。兩者都可以執行快取的“放入”操作,不同於@Cacheable,@CachePut每次都將執行方法並將返回值K-V放入快取,如果該K存在則進行更新。其他屬性可參考Cacheable。
@CacheEvict
@CachEvict主要針對方法配置,能夠根據一定的條件對特定的快取進行清空。該註解有兩個特別的屬性:
- allEntries 是否清空所有快取內容,預設為 false,如果指定為 true,則方法呼叫後將立即清空所有快取。注意不能跟key引數同時使用。
- beforeInvocation 是否在方法執行前就清空,預設為 false,如果指定為 true,則在方法還沒有執行的時候就清空快取,預設情況下,如果方法執行丟擲異常,則不會清空快取。
@Caching
該註解是個組合註解。有時候我們需要在一個方法上同時使用多個相同註解但是java是不支援一個註解在同一個方法上多次使用。這時就可以使用該註解進行組合。
使用要點
- 快取註解所在的方法不能在類中進行內部呼叫。
- 快取一定要有過期超時策略,避免系統不堪重負。
- 快取的值如果是集合考慮對集合的大小的限制,避免序列化/反序列化效能。
快取實戰
接下來我們通過Spring cache 集合redis 來實戰一下,甚至有一些特別的玩法。假設redis環境已經搭建好了。Spring Boot 專案中引入:
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- lettuce 必備依賴 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
在yml配置中我們大多可使用預設配置。配置spring.cache.type=REDIS
。其他配置可通過字首
spring.cache
、spring.redis
進行配置。
使用非阻塞反應式redis客戶端
Spring Boot 2.x中 預設使用lettuce作為預設redis客戶端。當然你也可以引入redisson客戶端。建議放棄阻塞客戶端jedis。
對快取進行自定義配置
如果我們使用預設的配置那麼所有的K-V都不會自動過期。很多情況下我們有這樣的需求,驗證碼快取5分鐘自動過期,區域資訊30分鐘。那麼我們就需要自定義 CacheManager。程式碼如下:
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.RedisCacheManagerBuilder.fromCacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
// 預設策略,未配置的 key 會使用這個
.cacheDefaults(redisConfig(60))
// 自定義 key 策略
.withInitialCacheConfigurations(redisCacheConfigurationMap()).build();
}
cacheDefaults方法用來指定預設配置,withInitialCacheConfigurations方法用來對各種快取空間進行個性化配置。redisCacheConfigurationMap方法是一個以快取名稱為key,其對應的redis配置類為值得鍵值對映。這個需要在開發中自己進行配置。參考CacheNameEnum
。
編寫快取處理類
經過上面的配置後,我們編寫以下快取類:
/**
* The type Captcha cache.
*
* @author dax
* @since 2019 /9/2 21:31
* @see cn.felord.rediscache.config.CacheNameEnum
*/
@Slf4j
@Component
@CacheConfig(cacheNames = {"smsCode"})
public class CaptchaCache {
@CachePut(key = "#key")
public String put(String key,String code){
log.info("執行 cachePut");
return code;
}
@CacheEvict(key = "#key")
public void expire(String key){
}
@Cacheable(key = "#key")
public String get(String key){
return null;
}
}
請注意 快取名稱 smsCode
在CacheNameEnum
進行了個性化配置。
總結
到上面我們的spring cache 快取就搞完了。樣例已經上傳到了我的碼雲倉庫,你可以通過以下地址:
https://gitee.com/felord/redis-cache 獲取demo,結合本文進行學習一些高階玩法來應對你的業務開發。
關注公眾號:碼農小胖哥 獲