SpringBoot系列(十五)整合快取,專案會用得到的技術

全棧學習筆記發表於2021-07-06

一、快取有什麼用?

 快取應該是我們每一個系統都應該考慮使用的,這樣可以加速系統的訪問,提升系統的效能。比如我們經常需要訪問的高頻資料,將此類資料放在快取中,可以大幅度提升系統的響應速度。原因就是一般來說我們的資料都是存在資料庫中,但是高頻的訪問資料庫不僅會對資料庫造成壓力,一定程度上還會影響響應速度,影響使用者體驗。如果引入了快取,不僅能提升訪問效能的同時降低資料庫的壓力。

二、JSR107規範

 JSR是Java Specification Requests的縮寫,意思是Java 規範提案。是指向JCP(Java Community Process)提出新增一個標準化技術規範的正式請求。任何人都可以提交JSR,以向Java平臺增添新的API和服務。JSR已成為Java界的一個重要標準。

規範:

 JCache規範定義了一種對Java物件臨時在記憶體中進行快取的方法,包括物件的建立、共享訪問、假離線(spooling)、失效、各JVM的一致性等,可被用於快取JSP內最經常讀取的資料,如產品目錄和價格列表。利用JCACHE,多數查詢的反應時間會因為有快取的資料而加快(內部測試表明反應時間大約快15倍)。

 Java Caching定義了5個核心介面,分別是CachingProvider, CacheManager, Cache, Entry 和 Expiry。其中CacheProvider就是快取提供器,其中包含了多個快取管理器(CacheManager),快取管理器之下才是我們需要用到的快取(Cache),快取的形式就是鍵值對,即entry物件(Entry),每一個entry物件會對應一個Expiry,即可以對每一條快取資料設定有效期。

​ 上面都是抄過來的廢話,看下面,實際操作一下。

三、Spring快取抽象

Spring 3.1開始定義了org.springframework.cache.Cache 和org.springframework.cache.CacheManager介面來統一不同的快取技術;並支援使用JCache(JSR-107)註解簡化我們開發。SpringBoot對快取整合提供了很好的支援,支援不同的快取規範(所謂的規範就是相當於面向介面程式設計,不同的快取有不同的實現)。

其中Cache介面為快取的元件規範定義,包含快取的各種操作集合;
Cache介面下Spring提供了各種xxxCache的實現;如RedisCache,EhCacheCache , ConcurrentMapCache等;下面我們來看看,SpringBoot中預設的一些快取配置與實現。預設的是SimpleCache。

四、SpringBoot中的快取-註解開發

新建SpringBoot web專案,加入依賴配置:SpringBoot版本為2.2.6.RELEASE 庫存文章了,版本是去年的。

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

然後我們逐個介紹一下快取開發中用到的一些註解。

  • @EnableCaching
  • @Cacheable
  • @CachePut
  • @CacheEvict
  • @Caching
  • @CacheConfig

1.@EnableCaching

 這個註解應該是最先了解的,把它放在啟動類上面,表示開啟基於註解的快取,然後你用那些快取相關的註解就能被Spring容器掃描到。這個註解不能缺,不然專案會報錯。

@EnableCaching
@SpringBootApplication
public class DemoCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoCacheApplication.class, args);
    }
}

2.@Cacheable

 這個註解有很多的屬性,可以定義一些快取的規則。主要是用於方法之上,將方法的結果作為快取的值,預設情況下將方法的引數作為快取的鍵(key),也就是key-value的形式儲存資料。首先介紹一下這個註解的執行機制:首先在方法執行之前,先根據註解配置的value或者是cacheNames到cacheManage中去查詢快取元件即Cache,當然如果第一次沒有這個快取就會自動建立。獲取到快取元件之後就用我們設定的key屬性,或者是keyGenerator策略生成的key去查詢鍵對應的快取值,對應上面的entry物件。如果查到了值就返回給請求客戶端,如果沒有查到這個值就呼叫方法,然後將方法返回的結果快取起來。

這裡我們來說一說這個註解裡面的一些屬性的用法:

  • value/cacheNames:這兩個屬性是一樣的,都是指定一個快取元件的名字,當然這個可以指定多個,是一個陣列。
  • key/keyGenerator:這兩個有一定的區別,但是用的時候只需要用其中一個就行了。key是我們可以直接指定這個快取結果對應的key是什麼,到時候直接去Cache裡面查就行了,keyGenerator就是可以自定義一個key的生成策略,然後生成key,兩者從結果上來看都是生成了key,所以兩者只需要用其中一個就OK了。
  • condition:在符合這個條件下才進行快取
  • unless:當這個條件為假的時候才進行快取,與condition相反

當然還有cacheManager(快取管理器)以及cacheResolver(快取解析器),這兩個都可以自己定義。兩個都差不多,二者用其中一個就OK。

下面我們來看看SpEl寫法,因為我們的key或者是condition,或者是unless屬性都會用到這個寫法。

來自官方的spel 語法

Name Location Description Example
methodName Root object 被呼叫方法的名稱 #root.methodName
method Root object 被呼叫的方法 #root.method.name
target Root object 被呼叫的目標物件 #root.target
targetClass Root object 被呼叫目標的類 #root.targetClass
args Root object 用於呼叫目標的引數(作為陣列) #root.args[0]
caches Root object 執行當前方法的快取集合 #root.caches[0].name
Argument name Evaluation context 任何方法引數的名稱。如果名稱不可用(可能是由於沒有除錯資訊),則引數名稱也可在代表引數索引的#a<#arg> where下#arg(從 開始0)。 #iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias).
result Evaluation context 方法呼叫的結果(要快取的值)。僅在unless 表示式、cache put表示式(用於計算key)或cache evict 表示式(何時beforeInvocationfalse)中可用。對於支援的包裝器(例如 Optional),#result指的是實際物件,而不是包裝器。 #result

具體使用如下:

@GetMapping("/cacheAble")
@Cacheable(value = "cache",key = "#root.args[0]",condition = "#id>20")
public  Student cacheAble(@RequestParam Integer id) {
    Student student = (Student) hashMap.get(id);
    System.out.println("第一次呼叫了方法,新增快取");
    return student;
}

上面就相當於是將第一個方法引數id作為快取的key ,返回值Student 作為值。快取的名稱就是cache,設定在引數id大於20的時候才進行快取,unless 則是相反的。

3.@CachePut

這個註解是用來更新快取的,一般情況下都是用在更新資料的介面上,示例如下:

@PostMapping("/putCache")
@CachePut(value = "cache",key = "#result.id")
public Student putCache(@RequestBody Student student) {
    hashMap.replace(student.getId(),student);
    System.out.println("呼叫了方法,也更新了快取");
    return student;
}

4.@CacheEvict

刪除快取的註解,可以設定刪除全部快取還是刪除部分快取資料。如下

@CacheEvict(value = "cache",key = "#id",beforeInvocation = true,allEntries = false)
@GetMapping("/cacheEvict")
public void cacheEvict(@RequestParam Integer id) {
    hashMap.remove(id);
    System.out.println("快取刪除");
}

beforeInvocation 設定為true ,表示在方法執行之前進行快取刪除,預設為false,``allEntries`設定為true ,表示刪除這個快取名稱下面的所有快取。

5.@Caching

藉助官方的話說,有時,需要指定多個相同型別的註解(例如@CacheEvict@CachePut)——例如,因為不同快取之間的條件或鍵表示式不同。@Caching允許在同一方法上使用多個巢狀的 @Cacheable@CachePut@CacheEvict註釋。實際上就是,各個快取註解可以配合使用,操作不同的快取空間。

 @Caching(
     cacheable = {
         @Cacheable(value = "cache",key = "#id"),
         @Cacheable(value = "cache1",key = "#id")
     },
     evict = {@CacheEvict(value = "cache2",key = "#id")}
 )
@GetMapping("/caching")
public Student caching(@RequestParam Integer id) {
    Student student = (Student)hashMap.get(id);
    System.out.println("新增快取了,也刪除了快取,但是操作的cache不一樣");
    return student;
}

程式碼實現的就是將資料快取到cache,cache1,再將cache2的快取刪除掉。

6.@CacheConfig

這是一個類級別的快取配置註解,像快取名稱,快取處理器(cacheResolver),快取管理器(cacheManager),這些配置可以直接配置到類上面,這樣方法對應的就能少些一些重複程式碼。

//註解的四個屬性
String[] cacheNames() default {};

String keyGenerator() default "";

String cacheManager() default "";
 
String cacheResolver() default "";

示例: 在類上面,加上註解,配置好對應的快取名稱,key生成策略等,keyGenerator,cacheManager,cacheResolver這三個東西留著下篇文章將redis快取的時候講,主要是比較多。

@CacheConfig(cacheNames = "chache")
public class CacheController {}

確實,我們的快取還能從cacheManager中獲取,這很好理解,cacheManager是管理快取的,當然也能獲取快取了。

@GetMapping("/get")
public Student getCache(@RequestParam Integer id){
    Cache cache = cacheManager.getCache("cache");
    Cache.ValueWrapper student = cache.get(id);
    Object o = student.get();
    return (Student) o;
}

由於SpringBoot的良好擴充套件性,CacheManager,CacheResolver,
KeyGenerator都可以自己配置,具體的下一篇文章會介紹。

相關文章