SpringBoot系列(十五)整合緩存,項目會用得到的技術


一、緩存有什么用?

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

二、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都可以自己配置,具體的下一篇文章會介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM