SpringBoot緩存管理(一) 默認緩存管理


前言

Spring框架支持透明地向應用程序添加緩存對緩存進行管理,其管理緩存的核心是將緩存應用於操作數據的方法(包括增刪查改等),從而減少操作數據的執行次數(主要是查詢,直接從緩存中讀取數據),同時不會對程序本身造成任何干擾

SpringBoot繼承了Spring框架的緩存管理功能,通過使用@EnableCaching注解開啟基於注解的緩存支持,SpringBoot就可以啟動緩存管理的自動化配置

接下來針對SpringBoot支持的默認緩存管理進行講解。

SpringBoot默認緩存管理

1、基礎環境搭建

(1)准備數據

使用前面 SpringBoot數據訪問(一) SpringBoot整合Mybatis 一文中創建的數據庫springbootdata,該數據庫中包含兩張數據表:t_article和t_comment。

(2)創建項目,代碼編寫

1、在項目依賴中添加SQL模塊的JPA依賴、MySql依賴以及Web模塊中的Web依賴,如下圖所示:

引入這三個依賴器創建項目,在項目pom.xml文件會出現以下依賴:

2、編寫數據庫表對應的實體類,並使用JPA相關注解配置映射關系

package com.hardy.springbootdatacache.entity;

import org.springframework.data.annotation.Id;

import javax.persistence.*;

/**
 * @Author: HardyYao
 * @Date: 2021/6/19
 */
@Entity(name = "t_comment") // 設置ORM實體類,並指定映射的表名
public class Comment {

    @Id // 映射對應的主鍵id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 設置主鍵自增策略
    private Integer id;

    private String content;

    private String author;

    @Column(name = "a_id")  // 指定映射的表字段名
    private Integer aId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Integer getaId() {
        return aId;
    }

    public void setaId(Integer aId) {
        this.aId = aId;
    }

    @Override
    public String toString() {
        return "Comment{" +
                "id=" + id +
                ", content='" + content + '\'' +
                ", author='" + author + '\'' +
                ", aId=" + aId +
                '}';
    }
}

3、編寫數據庫操作的Repository接口文件

package com.hardy.springbootdatacache.repository;

import com.hardy.springbootdatacache.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author: HardyYao
 * @Date: 2021/6/19
 */
public interface CommentRepository extends JpaRepository<Comment, Integer> {

    /**
     * 根據評論id修改評論作者author
     * @param author
     * @param id
     * @return
     */
    @Transactional
    @Modifying
    @Query("update t_comment c set c.author = ?1 where c.id=?2")
    int updateComment(String author,Integer id);

}

4、編寫service層

package com.hardy.springbootdatacache.service;

import com.hardy.springbootdatacache.entity.Comment;
import com.hardy.springbootdatacache.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * @Author: HardyYao
 * @Date: 2021/6/19
 */
@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    /**
     * 根據評論id查詢評論
     * @Cacheable:將該方法的查詢結果comment存放在SpringBoot默認緩存中
     * cacheNames:起一個緩存命名空間,對應緩存唯一標識
     * @param id
     * @return
     */
    @Cacheable(cacheNames = "comment")
    public Comment findCommentById(Integer id){
        Optional<Comment> comment = commentRepository.findById(id);
        if(comment.isPresent()){
            Comment comment1 = comment.get();
            return comment1;
        }
        return null;
    }

}

5、編寫controller層

package com.hardy.springbootdatacache.controller;

import com.hardy.springbootdatacache.entity.Comment;
import com.hardy.springbootdatacache.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: HardyYao
 * @Date: 2021/6/19
 */
@RestController
public class CommentController {

    @Autowired
    private CommentService commentService;

    @RequestMapping(value = "/findCommentById")
    public Comment findCommentById(Integer id){
        Comment comment = commentService.findCommentById(id);
        return comment;
    }

}

6、編寫配置文件

在全局配置文件application.properties中編寫對應的數據庫連接配置

# MySQL數據庫連接配置
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
# 顯示使用JPA進行數據庫查詢的SQL語句
spring.jpa.show-sql=true
 
# 開啟駝峰命名匹配映射
mybatis.configuration.map-underscore-to-camel-case=true
# 解決中文亂碼問題
spring.http.encoding.force-response=true

7、測試

在瀏覽器中輸入:http://localhost:8080/findCommentById?id=1 進行訪問(連續訪問三次):

在上圖中,因為沒有在SpringBoot項目中開啟緩存管理,故雖然數據表中的數據沒有任何變化,但是每執行一次查詢操作,即便執行的是相同的SQL語句,都還是會訪問一次數據庫。

2、默認緩存使用

在前面搭建的Web應用的基礎上,開啟SpringBoot默認支持的緩存,以使用SpringBoot默認緩存。

1、在項目啟動類的類名上方使用@EnableCaching注解開啟基於注解的緩存支持

package com.hardy.springbootdatacache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching  // 開啟SpringBoot基於注解的緩存管理支持
@SpringBootApplication
public class SpringbootdataCacheApplication {

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

}

2、使用@Cacheable注解對數據操作方法進行緩存管理

將@Cacheable注解標注在Service類的查詢方法上,對查詢結果進行緩存:

package com.hardy.springbootdatacache.service;

import com.hardy.springbootdatacache.entity.Comment;
import com.hardy.springbootdatacache.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * @Author: HardyYao
 * @Date: 2021/6/19
 */
@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    /**
     * 根據評論id查詢評論
     * @param id
     * @return
     */
    @Cacheable(cacheNames = "comment")
    public Comment findCommentById(Integer id){
        Optional<Comment> comment = commentRepository.findById(id);
        if(comment.isPresent()){
            Comment comment1 = comment.get();
            return comment1;
        }
        return null;
    }

}

3、測試訪問

在瀏覽器中輸入:http://localhost:8080/findCommentById?id=1 進行訪問(連續訪問三次):

可以看到,在使用SpringBoot默認緩存注解后,重復進行同樣的查詢操作,數據庫只執行了一次SQL查詢語句,說明項目開啟的默認緩存支持已生效。

SpringBoot默認緩存底層結構:在諸多的緩存自動配置類中,SpringBoot默認裝配的是SimpleCacheConfiguration,它使用的CacheManager是ConcurrentMapCacheManager,使用ConcurrentMap作為底層的數據結構,根據Cache的名字查詢出Cache,每一個Cache中存在多個key-value鍵值對、緩存值。

4、緩存注解介紹

前面我們通過使用@EnableCaching、@Cacheable注解實現了SpringBoot默認的基於注解的緩存管理,除此之外,還有其它注解及注解屬性也可用於配置優化緩存管理。下面,我們對@EnableCaching、@Cacheable及其他與緩存管理相關的注解進行介紹。

4.1、@EnableCaching注解

@EnableCaching注解是由Spring框架提供的,SpringBoot框架對該注解進行了繼承,該注解需要配置在類的上方(一般配置在項目啟動類上),用於開啟基於注解的緩存支持。

4.2、@Cacheable注解

@Cacheable注解也是由Spring框架提供的,可以作用於類或方法上(通常作用於數據查詢方法上),用於對方法的執行結果進行數據緩存存儲。注解的執行順序是:先進行緩存查詢,如果為空則進行方法查詢(查數據庫),並將結果進行緩存;如果緩存中有數據,則不進行方法查詢,而是直接使用緩存數據。

@Cacheable注解提供了多個屬性,用於對緩存存儲進行相關設置,如下所示:

執行流程&時機

方法運行之前,先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取,(CacheManager先獲取相應的緩存),第一次獲取緩存如果獲取不到,Cache組件會自動創建。

去Cache中查找緩存的內容,使用一個key進行查找,默認在只有一個參數的情況下,key值默認就是方法的參數;如果有多個參數或者沒有參數,則SpringBoot會按照某種策略生成key,默認是使用KeyGenerator生成的,其實現類為SimpleKeyGenerator。

SimpleKeyGenerator生成key的默認策略:

常用的SPEL表達式:

4.3、@CachePut注解

目標方法執行完之后生效。@CachePut被使用於修改操作較多,若緩存中已經存在目標值了,該注解作用的方法依然會執行,執行后將結果保存在緩存中(覆蓋掉原來的目標值)。

@CachePut注解也提供了多個屬性,這些屬性與@Cacheable注解的屬性完全相同。

4.4、@CacheEvict注解

@CacheEvict注解也是由Spring框架提供的,可以作用於類或方法上(通常作用於數據刪除方法上),該注解的作用是刪除緩存數據。

@CacheEvict注解的默認執行順序是:先進行方法調用,然后將緩存進行清除。


免責聲明!

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



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