首頁 > 軟體

SpringBoot詳解整合Redis快取方法

2022-07-15 14:05:42

1、Spring Boot支援的快取元件

在Spring Boot中,資料的快取管理儲存依賴於Spring框架中cache相關的org.springframework.cache.Cache和org.springframework.cache.CacheManager快取管理器介面。

如果程式中沒有定義型別為CacheManager的Bean元件或者是名為cacheResolver的CacheResolver快取解析器,Spring Boot將嘗試選擇並啟用以下快取元件(按照指定的順序):

(1)Generic

(2)JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)

(3)EhCache 2.x

(4)Hazelcast

(5)Infinispan

(6)Couchbase

(7)Redis

(8)Caffeine

(9)Simple

上面按照Spring Boot快取元件的載入順序,列舉了支援的9種快取元件,在專案中新增某個快取管理元件(例如Redis)後,Spring Boot專案會選擇並啟用對應的快取管理器。如果專案中同時新增了多個快取元件,且沒有指定快取管理器或者快取解析器(CacheManager或者cacheResolver),那麼Spring Boot會按照上述順序在新增的多個快取中優先啟用指定的快取元件進行快取管理。

剛剛講解的Spring Boot預設快取管理中,沒有新增任何快取管理元件能實現快取管理。這是因為開啟快取管理後,Spring Boot會按照上述列表順序查詢有效的快取元件進行快取管理,如果沒有任何快取元件,會預設使用最後一個Simple快取元件進行管理。Simple快取元件是Spring Boot預設的快取管理元件,它預設使用記憶體中的ConcurrentMap進行快取儲存,所以在沒有新增任何第三方快取元件的情況下,可以實現記憶體中的快取管理,但是我們不推薦使用這種快取管理方式

2、基於註解的Redis快取實現

在Spring Boot預設快取管理的基礎上引入Redis快取元件,使用基於註解的方式講解Spring Boot整合Redis快取的具體實現

(1)新增Spring Data Redis依賴啟動器。在pom.xml檔案中新增Spring Data Redis依賴啟動器

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

當我們新增進redis相關的啟動器之後, SpringBoot會使用RedisCacheConfigratioin 當做生效的自動設定類進行快取相關的自動裝配,容器中使用的快取管理器是RedisCacheManager , 這個快取管理器建立的Cache為 RedisCache , 進而操控redis進行資料的快取

(2)Redis服務連線設定

# Redis服務地址
spring.redis.host=127.0.0.1
# Redis伺服器連線埠
spring.redis.port=6379
# Redis伺服器連線密碼(預設為空)
spring.redis.password=

(3)對CommentService類中的方法進行修改使用@Cacheable、@CachePut、@CacheEvict三個註解客製化快取管理,分別進行快取儲存、快取更新和快取刪除的演示

package com.lagou.service;
import com.lagou.pojo.Comment;
import com.lagou.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CommentService {
    @Autowired
    private CommentRepository commentRepository;
    /**
     * @Cacheable: 將該方法查詢結果comment存放在SpringBoot預設快取中
     * cacheNames: 起一個快取的名稱空間,對應快取的唯一標識
     * value:快取結果   key:預設只有一個引數的情況下,key值預設就是方法引數值; 如果沒有引數或者多個引數的情況:會使用SimpleKeyGenerate來為生成key
     */
    @Cacheable(cacheNames = "comment", unless = "#result==null")
    public Comment findCommentById(Integer id) {
        Optional<Comment> byId = commentRepository.findById(id);
        if (byId.isPresent()) {
            Comment comment = byId.get();
            return comment;
        }
        return null;
    }
    // 更新方法
    @CachePut(cacheNames = "comment", key = "#result.id")
    public Comment updateComment(Comment comment) {
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        return comment;
    }
    // 刪除方法
    @CacheEvict(cacheNames = "comment")
    public void deleteComment(Integer id) {
        commentRepository.deleteById(id);
    }
}

以上 使用@Cacheable、@CachePut、@CacheEvict註解在資料查詢、更新和刪除方法上進行了快取管理。

其中,查詢快取@Cacheable註解中沒有標記key值,將會使用預設引數值comment_id作為key進行資料儲存,在進行快取更新時必須使用同樣的key;同時在查詢快取@Cacheable註解中,定義了“unless = "#result==null"”表示查詢結果為空不進行快取

控制層

package com.lagou.controller;
import com.lagou.pojo.Comment;
import com.lagou.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CommentController {
    @Autowired
    private CommentService commentService;
    @RequestMapping( "/findCommentById")
    public Comment findCommentById(Integer id) {
        Comment comment = commentService.findCommentById(id);
        return comment;
    }
    @RequestMapping(value = "/updateComment")
    public Comment updateComment(Comment comment) {
        Comment commentById = commentService.findCommentById(comment.getId());
        commentById.setAuthor(comment.getAuthor());
        return commentService.updateComment(commentById);
    }
    @RequestMapping(value = "/deleteComment")
    public void deleteComment(Integer id) {
        commentService.deleteComment(id);
    }
}

(4) 基於註解的Redis查詢快取測試

可以看出,查詢使用者評論資訊Comment時執行了相應的SQL語句,但是在進行快取儲存時出現了IllegalArgumentException非法引數異常,提示資訊要求對應Comment實體類必須實現序列化(“DefaultSerializer requires a Serializable payload but received an object of type”)。

(5)將快取物件實現序列化。

(6)再次啟動測試

存取“http://localhost:8080/findCommentById?id=1”查詢id為1的使用者評論資訊,並重復重新整理瀏覽器查詢同一條資料資訊,查詢結果

檢視控制檯列印的SQL查詢語句

還可以開啟Redis使用者端視覺化管理工具Redis Desktop Manager連線本地啟用的Redis服務,檢視具體的資料快取效果

執行findById()方法查詢出的使用者評論資訊Comment正確儲存到了Redis快取庫中名為comment的名稱空間下。其中快取資料的唯一標識key值是以“名稱空間comment::+引數值(comment::1)”的字串形式體現的,而value值則是經過JDK預設序列格式化後的HEX格式儲存。這種JDK預設序列格式化後的資料顯然不方便快取資料的視覺化檢視和管理,所以在實際開發中,通常會自定義資料的序列化格式

(7) 基於註解的Redis快取更新測試。

先通過瀏覽器存取“http://localhost:8080/updateComment/1/shitou”更新id為1的評論作者名為shitou;

接著,繼續存取“http://localhost:8080/findCommentById?id=1”查詢id為1的使用者評論資訊

可以看出,執行updateComment()方法更新id為1的資料時執行了一條更新SQL語句,後續呼叫findById()方法查詢id為1的使用者評論資訊時沒有執行查詢SQL語句,且瀏覽器正確返回了更新後的結果,表明@CachePut快取更新設定成功

(8)基於註解的Redis快取刪除測試

通過瀏覽器存取“http://localhost:8080/deleteComment?id=1”刪除id為1的使用者評論資訊;

執行deleteComment()方法刪除id為1的資料後查詢結果為空,之前儲存在Redis資料庫的comment相關資料也被刪除,表明@CacheEvict快取刪除成功實現

通過上面的案例可以看出,使用基於註解的Redis快取實現只需要新增Redis依賴並使用幾個註解可以實現對資料的快取管理。另外,還可以在Spring Boot全域性組態檔中設定Redis有效期,範例程式碼如下:

# 對基於註解的Redis快取資料統一設定有效期為1分鐘,單位毫秒
spring.cache.redis.time-to-live=60000

上述程式碼中,在Spring Boot全域性組態檔中新增了“spring.cache.redis.time-to-live”屬性統一設定Redis資料的有效期(單位為毫秒),但這種方式相對來說不夠靈活

3、基於API的Redis快取實現

在Spring Boot整合Redis快取實現中,除了基於註解形式的Redis快取實現外,還有一種開發中常用的方式——基於API的Redis快取實現。這種基於API的Redis快取實現,需要在某種業務需求下通過Redis提供的API呼叫相關方法實現資料快取管理;同時,這種方法還可以手動管理快取的有效期。

下面,通過Redis API的方式講解Spring Boot整合Redis快取的具體實現

(1)使用Redis API進行業務資料快取管理。在com.lagou.service包下編寫一個進行業務處理的類ApiCommentService

package com.lagou.service;
import com.lagou.pojo.Comment;
import com.lagou.repository.CommentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
public class ApiCommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    // 使用API方式進行快取,先去快取中查詢,快取中有,直接返回;沒有查詢資料庫
    public Comment findCommentById(Integer id) {
        Object o = redisTemplate.opsForValue().get("comment_" + id);
        if (o != null) {
            return (Comment) o; // 查詢到資料
        } else {
            // 快取中沒有,從資料庫中查詢
            Optional<Comment> byId = commentRepository.findById(id);
            if (byId.isPresent()) {
                Comment comment = byId.get();
                // 將查詢結果存入到快取中,同時還可以設定有效期
                redisTemplate.opsForValue().set("comment_" + id, comment, 1, TimeUnit.DAYS); // 過期時間為一天
                return comment;
            }
        }
        return null;
    }
    // 更新方法
    public Comment updateComment(Comment comment) {
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        // 將更新資料進行快取更新
        redisTemplate.opsForValue().set("comment_" + comment.getId(), comment, 1, TimeUnit.DAYS);
        return comment;
    }
    // 刪除方法
    public void deleteComment(Integer id) {
        commentRepository.deleteById(id);
        redisTemplate.delete("comment_" + id);
    }
}

(2)編寫Web存取層Controller檔案

package com.lagou.controller;
import com.lagou.pojo.Comment;
import com.lagou.service.ApiCommentService;
import com.lagou.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiCommentController {
    @Autowired
    private ApiCommentService commentService;
    @RequestMapping( "/findCommentById")
    public Comment findCommentById(Integer id) {
        Comment comment = commentService.findCommentById(id);
        return comment;
    }
    @RequestMapping(value = "/updateComment")
    public Comment updateComment(Comment comment) {
        Comment commentById = commentService.findCommentById(comment.getId());
        commentById.setAuthor(comment.getAuthor());
        return commentService.updateComment(commentById);
    }
    @RequestMapping(value = "/deleteComment")
    public void deleteComment(Integer id) {
        commentService.deleteComment(id);
    }
}

基於API的Redis快取實現的相關設定。基於API的Redis快取實現不需要@EnableCaching註解開啟基於註解的快取支援,所以這裡可以選擇將新增在專案啟動類上的@EnableCaching進行刪除或者註釋

到此這篇關於SpringBoot詳解整合Redis快取方法的文章就介紹到這了,更多相關SpringBoot Redis快取內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com