SpringBoot 整合ehcache 3.x

SpringBoot 整合ehcache 3.x

注:如果您發現任何不正確的內容,或者您想要分享有關本文主題的更多信息,請撰寫評論或聯繫我 [email protected]

以下環境基於 windows 10 java version “1.8.0_171” 代碼編輯器爲idea

文中代碼可能不太嚴謹,僅供研究測試使用!

背景

​ 在編寫後臺項目過程中,遇到後臺某個頁面會檢索大量數據到頁面,導致頁面反應速度變慢,且這些數據屬於變動不頻繁的(很少有刪、改、增之類的操作),爲了更好的提供服務,以最快的速度展示數據,所以想到了添加緩存。

​ 項目使用的是springBoot,就就怎麼方便怎麼來吧。用spring官方的總比自己寫的能硬、更堅挺、更持久!

Cache Abstraction,自spring3.1開始支持像開啓事務那樣非侵入的添加緩存。spring原話如下

從3.1版開始,Spring框架就支持向現有Spring應用程序透明地添加緩存。與事務支持類似,緩存抽象允許一致地使用各種緩存解決方案,對代碼的影響最小。

從Spring 4.1開始,在JSR-107註釋和更多定製選項的支持下,緩存抽象得到了顯著擴展。

是什麼?爲什麼使用它

​ 這塊解釋的內容有點照抄spring,不過都是爲了儘快學習掌握。

緩衝和緩存是什麼鬼

緩衝

緩衝區就像你在網上看小視頻,比如pornhub之類的,視頻需要加載速度,你看的是4K的。而你又很猴急,

​ 4K,是一種高清顯示技術。主要應用於電視行業、電影行業、手機行業等。作爲電視行業顯示技術的革命性突破,4K已經成爲行業內的常青樹,熱度從2012年開始就一直是有增無減。畫質技術作爲電視的核心要素,與3D、多屏互動等技術相比,畫質技術給人們帶來的不是一時新鮮感,它是從本質上提升電視的表現力,讓用戶能夠感受到最優秀的畫質所帶來的視覺盛宴。

這個時候,如果沒有緩存的話,網站加載一幀你看一幀。這對於看小片快進的你來說很難受,但是有了緩存之後(還有各種技術),網站會直接加載一分鐘(對於你來說是直接看到有十分鐘,其實還是一幀一幀,攢到十幀纔給你看),你就看的很爽了,直接就完事了。可能解釋的不太好。。。看看spring的解釋

​ 術語“緩衝區”和“緩存”往往可以互換使用。但是請注意,它們代表不同的東西。傳統上,緩衝區用作數據在快實體和慢實體之間的中間臨時存儲。由於一方必須等待另一方(這會影響性能),緩衝區允許整個數據塊(而不是小塊)同時移動,從而緩解了這種情況。數據只從緩衝區寫入和讀取一次。此外,至少有一方知道緩衝區是可見的。

另一方面,緩存根據定義是隱藏的,並且任何一方都不知道緩存的發生。它還提高了性能,但是通過讓相同的數據以快速方式多次讀取來提高性能。

抽象

抽象緩存 是基於方法的,會保存方法的入參以及其結果,如果下次有相同的請求過來直接返回已經存儲的結果(在沒有清空緩存的情況下)。

​ 緩存抽象的核心是將緩存應用於Java方法,從而根據緩存中可用的信息減少執行的次數。也就是說,每次調用目標方法時,抽象都會應用緩存行爲,檢查方法是否已經爲給定的參數執行。如果已執行,則返回緩存的結果,而不必執行實際的方法。如果方法尚未執行,則執行該方法,並緩存結果並返回給用戶,以便在下次調用該方法時返回緩存的結果。這樣,對於給定的一組參數,昂貴的方法(無論是CPU綁定的還是io綁定的)只能執行一次,並且結果可以重用,而不必實際再次執行該方法。緩存邏輯被透明地應用,沒有任何對調用程序的干擾。

​ 這種方法只適用於保證爲給定輸入(或參數)返回相同輸出(結果)的方法,無論執行了多少次。

​ 簡單理解了緩存接下來進入正題。

什麼是ehcache

官網

Ehcache是一種基於標準的開源緩存,可提高性能,卸載數據庫並簡化可伸縮性。它是使用最廣泛的基於Java的緩存,因爲它功能強大,經過驗證,功能齊全,並與其他流行的庫和框架集成。Ehcache可以從進程內緩存擴展到使用TB級緩存的混合進程內/進程外部署。

爲什麼使用ehcahe

​ 如果要你自己實現緩存你會如何選擇,創建個自定義註解然後aop監聽,將查詢入參和結果放入ConcurrentHashMap抑或是redis,在對數據的update、insert、delete時刪除緩存。你能想到的這些早有人做出來了,能用已經成熟的東西幹嘛非要造輪子。

​ 至於爲啥用ehcache,我順手行不,用redis還得搭建redis,麻煩。。。。

怎麼樣

spring提供緩存功能,如果還沒有定義類型爲CacheManager的bean 或名爲CacheResolver的CacheResolver的CacheResolver。Spring Boot嘗試檢測以下提供者(按照指定的順序)

Generic
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
EhCache 2.x
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Simple
  • Generic

  • JCache

  • EhCache 2.x

  • Hazelcast

  • Infinispan

  • Couchbase

  • Redis

  • Caffeine

  • Simple

    如果找不到其他提供程序,則配置一個使用ConcurrentHashMap作爲緩存存儲的簡單實現。如果應用程序中沒有緩存庫,這是默認設置 。至於怎麼默認找到這個 SimpleCacheConfiguration 想要了解原理,你首先得搞明白springBoot 是如何自動配置的,現階段知道是默認就行。有興趣可以看看 org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration#cacheManager

我們先使用默認的 cache。 使用idea直接創建springBoot腳手架,並引入pom依賴

  • spring-boot-starter-cache

使用spring cache緩存

  1. 我的pom依賴如下,或者你可以去maven倉庫找最新版本的ehcache。這個demo將會先做一個簡單的演示,然後在進行深入探討(先知道這玩意兒怎麼用,達到什麼效果,在解釋怎麼用,爲什麼這樣用)。
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>
    
<dependencies>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
    </dependencies>
  1. 在 DemoApplication.java 類上加註解@EnableCaching 聲明啓用緩存。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching // 開啓基於註解的緩存
@SpringBootApplication
public class DemoApplication {

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

}
  1. 新建一個簡單的測試controller TestController.java(與 DemoApplication.java 同包,或者在其包包含範圍內)
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController // @RequestMapping 和 @ResponseBody 
public class TestController {

    @RequestMapping("/{demoInt}")
    public String test(@PathVariable Integer demoInt) { // 接受路徑的參數做乘法運算
        System.out.println("我要運算了~~~");
        return String.valueOf(demoInt * 666);
    }
}
  1. 啓動項目。瀏覽器訪問 http://127.0.0.1:8080/2 項目可以正常運行,接下來加入緩存。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController
public class TestController {

    @RequestMapping("/{demoInt}")
    // @Cacheable註解會先查詢是否已經有緩存,有會使用緩存,沒有則會執行方法並緩存。
    @Cacheable(value = "operation", // 緩存的名稱,在 spring配置文件中定義,必須指定至少一個
               key = "#demoInt") // 緩存的 key,可以爲空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合。
    public String test(@PathVariable Integer demoInt) {
        System.out.println("我要運算了~~~");
        return String.valueOf(demoInt * 666);
    }
    // 此處的入參一定要實現 java.io.Serializable 。 Integer類 繼承抽象類 Number。 Number實現 Serializable
  1. 重新啓動項目,第一次訪問,瀏覽器打印
我要運算了~~~

​ 未重啓,刷新頁面第二次訪問,控制檯沒有任何輸出。這就說明緩存成功。

使用ehcache 3 緩存

  1. 修改pom,添加ehcache座標,這裏我們使用ehcache 3.X ,不使用 ehcache 2.x

    net.sf.ehcache(Ehcache 2.x)

    org.ehcache(Ehcache 3.x)

    我們需要spring-boot-starter-cache和cache-api依賴,以及依賴ehcache作爲緩存提供者。

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.8.0</version>
</dependency>

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>

  1. 修改配置文件application.yml

    spring:
      cache:
        jcache: # 這裏是 jcache 不是 ecache。我們使用的 ecache 3.x
          config: classpath:ehcache.xml
    
  2. 新建ehcache.xml

    <config
            xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
            xmlns='http://www.ehcache.org/v3'
            xsi:schemaLocation="
                http://www.ehcache.org/v3
                http://www.ehcache.org/schema/ehcache-core-3.7.xsd">
    
        <!-- 一個緩存模板。如果應用程序有多個緩存,但緩存的配置基本相同,那麼這將特別有利。  -->
    
        <!-- 持久緩存目錄 persistence(持久性標記) 在持久性標記中,我們爲硬盤(磁盤存儲)上基於文件的緩存定義了目錄。這只是文件夾的定義。-->
        <persistence directory="spring-boot-ehcache/cache" />
    
       <!-- Default cache template -->
        <cache-template name="default">
            <expiry>
                <ttl unit="seconds">30</ttl> <!-- 在expires標籤中,我們定義了30秒的生存時間(ttl)。生存時間指定緩存條目獨立於訪問可以在緩存中保留多長時間。指定的時間過期後,將從緩存中刪除該值。 -->
            </expiry>
            <resources> <!-- 配置緩存的層和容量。 -->
                <heap>1000</heap> <!-- 對於堆上存儲,我們配置了1,000個緩存條目的容量。這是開始刪除緩存之前的最大條目數。 -->
                <offheap unit="MB">10</offheap> <!-- 對於堆外存儲,我們配置了10mb的容量。 -->
                <disk persistent="true" unit="MB">20</disk> <!-- 作爲磁盤緩存,我們配置了20mb 磁盤緩存必須始終具有比堆緩存更高的內存容量,否則應用程序在解析XML文件時在應用程序啓動時拋出異常。 -->
            </resources>
        </cache-template>
    
    </config>
    
  3. 完成以上配置。實際上還不能夠使用緩存。還需要在ehcache.xml 添加

    <!-- Cache configuration -->
        <cache alias="operation" uses-template="default"> <!-- 使用上面配置的模版 -->
    
        </cache>
    

    這裏的 operation 是 TestController.test() 上@Cacheable註解的value值。 這裏務必要加上。到這裏基本就可以滿足基本的要求。

如何添加緩存

​ 在指定需要添加緩存的方法上加上註釋,此處加的註釋與上文中SpringBoot使用默認的ConcurrentHashMap 相同

@RequestMapping("/{demoInt}")
    // @Cacheable註解會先查詢是否已經有緩存,有會使用緩存,沒有則會執行方法並緩存。
    @Cacheable(value = "operation",// 緩存的名稱,在 spring配置文件中定義,必須指定至少一個
            key = "#demoInt") // 緩存的 key,可以爲空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合。
    public String test(@PathVariable Integer demoInt) {
        // 此處的入參一定要實現 java.io.Serializable 。 Integer類 繼承抽象類 Number。 Number實現 Serializable
        System.out.println("我要運算了~~~");
        return String.valueOf(demoInt * 666);
    }

​ 此處的 @Cacheable 的value值一定要在 ehcache.xml 中cache標籤alias聲明

<!-- Cache configuration -->
    <cache alias="operation" uses-template="default"> <!-- 使用上面配置的模版 -->

    </cache>

如何清除緩存

使用 @CacheEvict 註解

@CachEvict 的作用 主要針對方法配置,能夠根據一定的條件對緩存進行清空 。

屬性 解釋 示例
allEntries 是否清空所有緩存內容,缺省爲 false,如果指定爲true,則方法調用後將立即清空所有緩存 @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法執行前就清空,缺省爲 false,如果指定爲 true,則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法執行拋出異常,則不會清空緩存 @CachEvict(value=”testcache”,beforeInvocation=true)

爲了方便測試,我新建了一個對象,並重新編寫了測試的Controller

import java.io.Serializable;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月12日
 */
public class Person implements Serializable { // 一定要實現 Serializable!!!!

    private static final long serialVersionUID = -680651108611576893L;
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Author: amarone
 * @Created Date: 2019年09月11日
 */
@RestController
public class TestController {

    @RequestMapping("/{demoInt}")
    // @Cacheable註解會先查詢是否已經有緩存,有會使用緩存,沒有則會執行方法並緩存。
    @Cacheable(value = "operation",// 緩存的名稱,在 spring配置文件中定義,必須指定至少一個
            key = "#demoInt") // 緩存的 key,可以爲空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合。
    public Object test(@PathVariable Integer demoInt) {
        // 此處的入參一定要實現 java.io.Serializable 。 Integer類 繼承抽象類 Number。 Number實現 Serializable
        Person person = new Person();
        person.setAge(demoInt);
        person.setName("張三" + demoInt);
        System.out.println(person.toString());
        return person;
    }

    /**
     * I'm going to kill all the data
     * 方法調用後清空所有緩存
     */
    @RequestMapping("/all")
    @CacheEvict(value = "operation", allEntries = true)
    public String cleanAll() {
        return "SUCCESS";
    }

    /**
     * kill one data
     */
    @RequestMapping("/kill/{demoInt}")
    @CacheEvict(value = "operation", key = "#demoInt")
    public String cleanOne(@PathVariable Integer demoInt) {
        return "SUCCESS";
    }

更多支持的註解以及用法請看 spring 官網

參考

@Cacheable 參數

Annotation parameter Description
value / cacheNames 要存儲方法執行結果的緩存的名稱
key The key for the cache entries as Spring Expression Language (SpEL). If the parameter is not specified, a key is created for all method parameters by default.
keyGenerator 實現密鑰生成器接口的bean的名稱,因此允許創建用戶定義的緩存密鑰。
condition Condition as Spring Expression Language (SpEL) that specifies when a result is to be cached.
unless Condition as Spring Expression Language (SpEL) that specifies when a result should not be cached.

參考鏈接

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