Spring Boot緩存

概述

??Spring提供了對(duì)應(yīng)用程序添加緩存的支持。從本質(zhì)上講,將緩存應(yīng)用于方法上,從而根據(jù)緩存中的信息減少執(zhí)行次數(shù)。當(dāng)開發(fā)者調(diào)用一個(gè)方法時(shí),將方法的參數(shù)和返回值作為Key/Value緩存起來(lái),當(dāng)再次調(diào)用這個(gè)方法時(shí),如果緩存中存在對(duì)應(yīng)數(shù)據(jù),就從緩存中獲取數(shù)據(jù),否則再去執(zhí)行該方法。

支持

Spring并沒(méi)有提供緩存的實(shí)現(xiàn),而是提供了一套Api,可以自由選擇緩存的實(shí)現(xiàn)。目前Spring Boot支持的緩存有如下幾種(Spring Boot會(huì)按順序檢測(cè)以下提供的程序):

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

可以通過(guò)設(shè)置屬性來(lái)指定提供緩存的程序。

無(wú)論使用哪種緩存實(shí)現(xiàn)不同的只是緩存配置,使用的緩存注解是一致的

注解 介紹
@EnableCaching 啟用S??pring的注釋驅(qū)動(dòng)的緩存管理功能
@CacheConfig 當(dāng)此批注出現(xiàn)在給定的類上時(shí),它為該類中定義的任何緩存操作提供一組默認(rèn)設(shè)置。
@Cacheable 表示可以緩存調(diào)用方法(或類中的所有方法)結(jié)果的注釋。每次調(diào)用指定方法時(shí),將進(jìn)行緩存行為,檢查是否已緩存該方法參數(shù)和結(jié)果。如果在緩存中找不到鍵的值,則將調(diào)用目標(biāo)方法并將返回的值存儲(chǔ)在關(guān)聯(lián)的緩存中。
@CacheEvict 表示方法(或類上的所有方法)觸發(fā)緩存逐出操作的注解。
@CachePut 表示方法(或類上的所有方法)觸發(fā)緩存放置操作的注釋。
@Caching 此批注可用作元批注,以創(chuàng)建具自定義組合批注。

一、Ehcache 2.x 緩存

在項(xiàng)目的pom.xml文件中添加以下依賴:
        <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>

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
添加緩存配置文件

如果存在Ehcache的依賴,并且在classpath下有名為ehcache.xml的文件,那么EhCacheCacheManager將會(huì)自動(dòng)作為緩存的提供者。因此,在resources目錄下創(chuàng)建ehcache.xml文件作為Ehcach緩存的配置文件。

如果未配置ehcache.xml,則Ehcache依賴包下的ehcache-failsafe.xml是ehcache的默認(rèn)配置。

<ehcache>
    <!--diskStore元素是可選的。-->
    <!--如果為任何緩存啟用了overflowToDisk或diskPersistent,則必須對(duì)其進(jìn)行配置。-->
    <!--diskStore只有一個(gè)屬性 - path。這是將在其中創(chuàng)建.data和.index文件的目錄路徑。-->
    <!--如果路徑是Java系統(tǒng)屬性,則將其替換為正在運(yùn)行的VM中的值-->
    <!--user.home - 用戶的主目錄-->
    <!--user.dir - 用戶的當(dāng)前工作目錄-->
    <!--java.io.tmpdir - 默認(rèn)臨時(shí)文件路徑-->
    <!--ehcache.disk.store.dir - 在命令行上指定的系統(tǒng)屬性(例:java -Dehcache.disk.store.dir=/u01/myapp/diskdir)-->
    <!--子目錄可以在屬性后指定 例:java.io.tmpdir/cache-->
    <diskStore path="java.io.tmpdir/cache"/>

    <!--強(qiáng)制性默認(rèn)緩存配置-->
    <!--這些設(shè)置將應(yīng)用于使用CacheManager.add(String cacheName)創(chuàng)建的緩存。-->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <!--localTempSwap - 該策略允許緩存在緩存操作期間使用本地磁盤。磁盤存儲(chǔ)是臨時(shí)的,并在重新啟動(dòng)后清除。-->
        <!--當(dāng)策略為"none"時(shí),所有緩存都保留在內(nèi)存中(磁盤無(wú)溢出,磁盤無(wú)持久性)。-->
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <cache name="student"
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>
</ehcache>
緩存配置 <cache/>:
屬性 介紹
name 設(shè)置緩存的名稱,用于標(biāo)識(shí)緩存。
maxElementsInMemory 設(shè)置在內(nèi)存中創(chuàng)建的最大對(duì)象數(shù)(0 ==無(wú)限制)。
maxElementsOnDisk 設(shè)置將在DiskStore中維護(hù)的最大對(duì)象數(shù)。默認(rèn)值為零,表示無(wú)限制。
eternal 設(shè)置元素是否一直存在。如果為true,超時(shí)將被忽略。
overflowToDisk 設(shè)置當(dāng)內(nèi)存中的緩存達(dá)到maxInMemory限制時(shí)元素是否可以溢出到磁盤。
timeToIdleSeconds 設(shè)置元素過(guò)期之前的空閑時(shí)間,即緩存創(chuàng)建以后最后一次訪問(wèn)緩存的時(shí)間到超時(shí)失效時(shí)的時(shí)間間隔。值為0表示無(wú)窮大,默認(rèn)值為0。
timeToLiveSeconds 設(shè)置元素過(guò)期之前的生存時(shí)間,即從創(chuàng)建時(shí)間到元素過(guò)期之間的間隔。值為0表示一直存在,默認(rèn)值為0。
diskPersistent 磁盤存儲(chǔ)在虛擬機(jī)重新啟動(dòng)后是否仍然存在,默認(rèn)值為false。
diskExpiryThreadIntervalSeconds 做元素失效監(jiān)測(cè)以及清除工作的線程運(yùn)行間隔時(shí)間,默認(rèn)值為120秒。
diskSpoolBufferSizeMB 磁盤緩沖區(qū)大小,默認(rèn)30MB(內(nèi)部以字節(jié)為單位,設(shè)置的值轉(zhuǎn)換為字節(jié)后不可超過(guò)正整數(shù)表示范圍)。
memoryStoreEvictionPolicy 達(dá)到maxElementsInMemory限制后,將強(qiáng)制執(zhí)行清除策略。默認(rèn)策略是最近最少使用(LRU),其他可用策略先進(jìn)先出(指定為FIFO)和不常用(指定為L(zhǎng)FU)。
開啟緩存

在項(xiàng)目的入口類上添加@EnableCaching注解開啟緩存。

@EnableCaching
@SpringBootApplication
public class EhcacheApplication {

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

}
創(chuàng)建數(shù)據(jù)接口
@Service
@CacheConfig(cacheNames = "student")
public class StudentService {

    @Resource
    private StudentMapper studentMapper;

    @Cacheable
    public Student findById(Integer id) {
        Student student = studentMapper.findById(id);
        System.out.println("查詢 :" + JSON.toJSONString(student));
        return student;
    }

    @CachePut(key = "#student.id")
    public Student insert(Student student) throws Exception {
        int status = studentMapper.insert(student);
        if (status == 0) {
            throw new Exception("Insert failed");
        }
        System.out.println("插入 :" + JSON.toJSONString(student));
        return student;
    }

    @CacheEvict(key = "#student.id")
    public Student delete(Student student) throws Exception {
        int status = studentMapper.delete(student);
        if (status == 0) {
            throw new Exception("Delete failed");
        }
        System.out.println("刪除 :" + JSON.toJSONString(student));
        return student;
    }

    @CachePut(key = "#student.id")
    public Student update(Student student) throws Exception {
        int status = studentMapper.update(student);
        if (status == 0) {
            throw new Exception("Update failed");
        }
        System.out.println("更新 :" + JSON.toJSONString(student));
        return student;
    }

}
創(chuàng)建請(qǐng)求接口
@RestController
@RequestMapping("/std")
public class StudentController {

    @Resource
    private StudentService studentService;

    /**
     * 查詢
     */
    @RequestMapping("/sel")
    public String findById(Student student) {
        Map<String, Object> msg = new HashMap<>();
        try {
            student = studentService.findById(student.getId());
            msg.put("status", 200);
            msg.put("result", student);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return JSON.toJSONString(msg);
    }

    /**
     * 插入
     */
    @RequestMapping("/ins")
    public String insert(Student student) {
        Map<String, Object> msg = new HashMap<>();
        try {
            studentService.insert(student);
            msg.put("status", 200);
            msg.put("message", "插入成功");
        } catch (Exception e) {
            msg.put("status", 400);
            msg.put("message", "插入失敗");
        }
        return JSON.toJSONString(msg);
    }


    /**
     * 刪除
     */
    @RequestMapping("/del")
    public String delete(Student student) {
        Map<String, Object> msg = new HashMap<>();
        try {
            studentService.delete(student);
            msg.put("status", 200);
            msg.put("message", "刪除成功");
        } catch (Exception e) {
            msg.put("status", 400);
            msg.put("message", "刪除失敗");
        }
        return JSON.toJSONString(msg);
    }

    /**
     * 更新
     */
    @RequestMapping("/upd")
    public String update(Student student) {
        Map<String, Object> msg = new HashMap<>();
        try {
            studentService.update(student);
            msg.put("status", 200);
            msg.put("message", "更新成功");
        } catch (Exception e) {
            msg.put("status", 400);
            msg.put("message", "更新失敗");
        }
        return JSON.toJSONString(msg);
    }

}
測(cè)試

向數(shù)據(jù)庫(kù)中添加數(shù)據(jù):

(一) 查詢

連續(xù)訪問(wèn)查詢接口頁(yè)面并觀察輸出信息:

查詢
控制臺(tái)

根據(jù)輸出信息發(fā)現(xiàn),在瀏覽器中多次獲取數(shù)據(jù),數(shù)據(jù)查詢方法只執(zhí)行了一遍。

(二) 更新

訪問(wèn)更新接口:

更新

再次訪問(wèn)查詢接口:

查詢

觀察控制臺(tái):


控制臺(tái)

發(fā)現(xiàn)訪問(wèn)更新接口后再次訪問(wèn)查詢接口,查詢接口并沒(méi)有再次從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),而是從緩存中獲取,所以更新接口返回的結(jié)果會(huì)覆蓋指定參數(shù)鍵的緩存。

(三) 插入

訪問(wèn)插入接口:

插入

訪問(wèn)查詢接口:

查詢

觀察控制臺(tái):

控制臺(tái)

發(fā)現(xiàn)插入數(shù)據(jù)后再次查詢并沒(méi)有從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù),而是從緩存中獲取。因?yàn)椴迦霑r(shí)對(duì)參數(shù)和返回的結(jié)果進(jìn)行了緩存。

(四) 刪除

訪問(wèn)刪除接口:

刪除

訪問(wèn)查詢接口:

查詢

觀察控制臺(tái):

控制臺(tái)

發(fā)現(xiàn)訪問(wèn)刪除接口后數(shù)據(jù)庫(kù)中的數(shù)據(jù)被刪除,再次訪問(wèn)查詢接口進(jìn)行查詢發(fā)現(xiàn)觸發(fā)了查詢方法,說(shuō)明刪除數(shù)據(jù)時(shí)緩存同時(shí)也被刪除了。

二、Redis 單機(jī)緩存

和Ehcache一樣,如果在classpath下存在Redis并且Redis已經(jīng)配置好,此時(shí)會(huì)默認(rèn)使用RedisCacheManager作為緩存的提供者。

在項(xiàng)目的pom.xml文件中添加以下依賴(與之前的依賴相比不同的只是緩存的提供者):

        <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>

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

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

添加項(xiàng)目配置:

spring:
  datasource:
    url: jdbc:mysql://xxx.xxx.xxx.xxx/user
    username: xxxx
    password: xxxxxx

  # 緩存配置
  cache:
    # 配置緩存名,多個(gè)緩存可使用逗號(hào)分隔(one,two)
    cache-names: student
    # 緩存存在時(shí)間
    redis:
      time-to-live: 120s
  redis:
    host: xxx.xxx.xxx.xxx
    port: xxxx

之后的流程與之前一致就不在闡述了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容