SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

系統環境:

  • Redis 版本:5.0.7
  • SpringBoot 版本:2.2.2.RELEASE

參考地址:

  • Redus 官方網址:https://redis.io/
  • 博文示例項目 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-redis-cache-example

一、緩存概念知識

1、是什麼緩存

我們日常生活中,經常會接觸聽到緩存這個詞,例如,瀏覽器清空緩存,處理器緩存大小,磁盤緩存等等。經過分類,可以將緩存分為:

  • 硬件緩存: 一般指的是機器上的 CPU、硬盤等等組件的緩存區間,一般是利用的內存作為一塊中轉區域,都通過內存交互信息,減少系統負載,提供傳輸效率。
  • 客戶端緩存: 一般指的是某些應用,例如瀏覽器、手機App、視頻緩衝等等,都是在加載一次數據後將數據臨時存儲到本地,當再次訪問時候先檢查本地緩存中是否存在,存在就不必去遠程重新拉取,而是直接讀取緩存數據,這樣來減少遠端服務器壓力和加快載入速度。
  • 服務端緩存: 一般指遠端服務器上,考慮到客戶端請求量多,某些數據請求量大,這些熱點數據經常要到數據庫中讀取數據,給數據庫造成壓力,還有就是 IO、網絡等原因有一定延遲,響應客戶端較慢。所以,在一些不考慮實時性的數據中,經常將這些數據存在內存中(內存速度非常快),當請求時候,能夠直接讀取內存中的數據及時響應。

2、為什麼使用緩存

用緩存,主要有解決 高性能 與 高併發 與 減少數據庫壓力。緩存本質就是將數據存儲在內存中,當數據沒有發生本質變化的時候,我們應儘量避免直接連接數據庫進行查詢,因為併發高時很可能會將數據庫壓塌,而是應去緩存中讀取數據,只有緩存中未查找到時再去數據庫中查詢,這樣就大大降低了數據庫的讀寫次數,增加系統的性能和能提供的併發量。

SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

3、緩存的優缺點

優點:

  • 加快了響應速度
  • 減少了對數據庫的讀操作,數據庫的壓力降低。

缺點:

  • 內存容量相對硬盤小。
  • 緩存中的數據可能與數據庫中數據不一致。
  • 因為內存斷電就清空數據,存放到內存中的數據可能丟失。

二、Redis 概念知識

1、什麼是 Redis

Redis 是一個高性能的 Key-Value 數據庫,它是完全開源免費的,而且 Redis 是一個 NoSQL 類型數據庫,是為了解決 高併發、高擴展,大數據存儲 等一系列的問題而產生的數據庫解決方案,是一個非關係型的數據庫。但是,它也是不能替代關係型數據庫,只能作為特定環境下的擴充。

2、為什麼使用 Redis 作為緩存

  • 支持高可用: Redis 支持 master\\slave 主\\從機制、sentinal 哨兵模式、cluster 集群模式,這樣大大保證了 Redis 運行的穩定和高可用行。
  • 支持多種數據結構: Redis 不僅僅支持簡單的 Key/Value 類型的數據,同時還提供 list、set、zset、hash 等數據結構的存儲。
  • 支持數據持久化: 可以將內存中的數據持久化在磁盤中,當宕機或者故障重啟時,可以再次加載進如 Redis,從而不會或減少數據的丟失。
  • 有很多工具與插件對其支持: Redis 已經在業界廣泛使用,已經是成為緩存的首選目標,所以很多語言和工具對其支持,我們只需要簡單的操作就可以輕鬆使用。
SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

3、Redis 支持的數據類型

Redis 支持的數據結構類型包括:

  • 字符串(string)
  • 哈希表(hash)
  • 列表(list)
  • 集合(set)
  • 有序集合(zset)

為了保證讀取的效率,Redis 把數據對象都存儲在內存當中,它可以支持週期性的把更新的數據寫入磁盤文件中。而且它還提供了交集和並集,以及一些不同方式排序的操作。

三、緩存後可能遇見的問題

1、緩存穿透

SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

緩存穿透: 指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。

緩存穿透幾種解決辦法:

  • 緩存空值,在從 DB 查詢對象為空時,也要將空值存入緩存,具體的值需要使用特殊的標識, 能和真正緩存的數據區分開,另外將其過期時間設為較短時間。
  • 使用布隆過濾器,布隆過濾器能判斷一個 key 一定不存在(不保證一定存在,因為布隆過濾器結構原因,不能刪除,但是舊值可能被新值替換,而將舊值刪除後它可能依舊判斷其可能存在),在緩存的基礎上,構建布隆過濾器數據結構,在布隆過濾器中存儲對應的 key,如果存在,則說明 key 對應的值為空。

2、緩存擊穿

SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

緩存擊穿: 某個 key 非常熱點,訪問非常頻繁,處於集中式高併發訪問的情況,當這個 key 在失效的瞬間,大量的請求就擊穿了緩存,直接請求數據庫,就像是在一道屏障上鑿開了一個洞。

緩存擊穿幾種解決辦法:

  • 設置二級緩存,或者設置熱點緩存永不過期,需要根據實際情況進行配置。
  • 使用互斥鎖,在執行過程中,如果緩存過期,那麼先獲取分佈式鎖,在執行從數據庫中加載數據,如果找到數據就存入緩存,沒有就繼續該有的動作,在這個過程中能保證只有一個線程操作數據庫,避免了對數據庫的大量請求。

3、緩存雪崩

SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

緩存雪崩:

當緩存服務器重啟、或者大量緩存集中在某一個時間段失效,這樣在失效的時候,也會給後端系統(比如DB)帶來很大壓力,造成數據庫後端故障,從而引起應用服務器雪崩。

緩存雪崩幾種解決辦法:

  • 緩存組件設計高可用,緩存高可用是指,存儲緩存的組件的高可用,能夠防止單點故障、機器故障、機房宕機等一系列問題。例如 Redis sentinel 和 Redis Cluster,都實現了高可用。
  • 請求限流與服務熔斷降級機制,限制服務請求次數,當服務不可用時快速熔斷降級。
  • 設置緩存過期時間一定的隨機分佈,避免集中在同一時間緩存失效。
  • 定時更新緩存策略,對於實時性要求不高的數據,定時進行更新。

4、緩存一致性

使用緩存很大可能導致數據不一致問題,如下:

  • 更熟數據庫成功 -> 更新緩存失敗 -> 數據不一致
  • 更新緩存成功 -> 更新數據庫失敗 -> 數據不一致
  • 更新數據庫成功 -> 淘汰緩存失敗 -> 數據不一致
  • 淘汰緩存成功 -> 更新數據庫失敗 -> 查詢緩存mis

所以使用緩存時候,應該結合實際情況,考慮緩存的數據是否有一致性需求。

四、SpringBoot 如何結合 Redis 實現緩存

1、Mavne 引入相關依賴

  • spring-boot-starter-data-redis
  • commons-pool2
SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

2、配置 Redis 參數

application 文件中添加連接 Redis 的配置參數

  • Redis 單機配置:
SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

  • Redis 哨兵配置:
  • SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

  • Redis 集群配置:
  • SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    3、配置 Spring 緩存管理器

    <code>@Configuration
    public class RedisConfig {

    /**
    * 配置緩存管理器
    * @param factory Redis 線程安全連接工廠
    * @return 緩存管理器
    */
    @Bean

    public CacheManager cacheManager(RedisConnectionFactory factory) {
    // 生成兩套默認配置,通過 Config 對象即可對緩存進行自定義配置
    RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
    // 設置過期時間 10 分鐘
    .entryTtl(Duration.ofMinutes(10))
    // 設置緩存前綴
    .prefixKeysWith("cache:user:")
    // 禁止緩存 null 值
    .disableCachingNullValues()
    // 設置 key 序列化
    .serializeKeysWith(keyPair())
    // 設置 value 序列化
    .serializeValuesWith(valuePair());
    // 返回 Redis 緩存管理器
    return RedisCacheManager.builder(factory)
    .withCacheConfiguration("user", cacheConfig).build();
    }

    /**
    * 配置鍵序列化
    * @return StringRedisSerializer
    */
    private RedisSerializationContext.SerializationPair<string> keyPair() {
    return RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
    }

    /**
    * 配置值序列化,使用 GenericJackson2JsonRedisSerializer 替換默認序列化
    * @return GenericJackson2JsonRedisSerializer
    */
    private RedisSerializationContext.SerializationPair<object> valuePair() {
    return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    }

    }
    /<object>/<string>/<code>

    4、服務中使用 SpringCache 的註解

    <code>@Service
    @CacheConfig(cacheNames = "user")
    public class UserServiceImpl implements UserService {

    /**
    * 新增用戶
    */
    public User addUser(User user) {
    ......
    }

    /**
    * 查詢用戶
    */
    @Cacheable(key = "#username")
    public User getUserByUsername(String username) {
    ......
    }

    /**
    * 更新用戶
    */
    @CachePut(key = "#user.username")
    public User updateUser(User user) {
    ......
    }

    /**
    * 刪除用戶
    */
    @CacheEvict(key = "#username")
    public void deleteByUsername(String username) {
    ......
    }

    }/<code>

    註解說明:

    • @CacheConfig: 一般配置在類上,指定緩存名稱,這個名稱是和上面“置緩存管理器”中緩存名稱的一致。
    • @Cacheable: 作用於方法上,用於對於方法返回結果進行緩存,如果已經存在該緩存,則直接從緩存中獲取,緩存的key可以從入參中指定,緩存的 value 為方法返回值。
    • @CachePut: 作用於方法上,無論是否存在該緩存,每次都會重新添加緩存,緩存的key可以從入參中指定,緩存的value為方法返回值,常用作於更新。
    • @CacheEvict: 作用於方法上,用於清除緩存
    • @Caching: 作用於方法上,用於一次性設置多個緩存。

    上面註解中的常用配置參數:

    • value: 緩存管理器中配置的緩存的名稱,這裡可以理解為一個組的概念,緩存管理器中可以有多套緩存配置,每套都有一個名稱,類似於組名,這個可以配置這個值,選擇使用哪個緩存的名稱,配置後就會應用那個緩存名稱對應的配置。
    • key: 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合。
    • condition: 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存。
    • unless: 不緩存的條件,和 condition 一樣,也是 SpEL 編寫,返回 true 或者 false,為 true 時則不進行緩存。

    5、啟動類添加開啟緩存註解

    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

  • @EnableCaching: 作用於類上,用於開啟註解功能。
  • 五、SpringCache 操作緩存的不足

    使用 Spring Cache 雖然方便,但是也有很多侷限性,因為它多是根據請求參數命名 key,根據返回指設置 value,這樣很多情況下,我們想方法內部進行命名和操作有一定的限制。如果我們需要靈活設置緩存,可以不用 SpringCache 提供的註解,直接在代碼中使用 Spring-data-redis 包提供的方法,手動操作 key 與 value。

    • opsForValue().set(String key, String value);
    • opsForValue().get(String key);
    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    還有經常要批量設置、讀取緩存,可以使用:

    • opsForValue().multiSet(Map map);
    • opsForValue().multiGet(List list);
    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    六、SpringBoot + SpringCache + Redis 示例項目

    下面是一個簡單的 SpringBoot 項目,用於對用戶的增刪改查,這裡使用 SpringCache 來模擬對數據進行緩存,示例如下:

    1、Mavne 引入相關依賴

    Maven 中引入 SpringBoot 和 Redis 依賴,因為使用了

    <code>
    <project> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0/<modelversion>

    <parent>
    <groupid>org.springframework.boot/<groupid>
    <artifactid>spring-boot-starter-parent/<artifactid>
    <version>2.2.2.RELEASE/<version>
    /<parent>

    <groupid>mydlq.club/<groupid>
    <artifactid>springboot-redis-example/<artifactid>
    <version>0.0.1/<version>
    <name>springboot-redis-example/<name>
    <description>Demo project for Spring Boot Redis/<description>

    <properties>
    <java.version>1.8/<java.version>
    /<properties>

    <dependencies>
    <dependency>
    <groupid>org.springframework.boot/<groupid>
    <artifactid>spring-boot-starter-web/<artifactid>
    /<dependency>
    <dependency>
    <groupid>org.springframework.boot/<groupid>
    <artifactid>spring-boot-starter-data-redis/<artifactid>
    /<dependency>
    <dependency>
    <groupid>org.apache.commons/<groupid>
    <artifactid>commons-pool2/<artifactid>
    /<dependency>
    <dependency>
    <groupid>org.projectlombok/<groupid>
    <artifactid>lombok/<artifactid>
    /<dependency>
    /<dependencies>

    <build>
    <plugins>
    <plugin>

    <groupid>org.springframework.boot/<groupid>
    <artifactid>spring-boot-maven-plugin/<artifactid>
    /<plugin>
    /<plugins>
    /<build>

    /<project>
    /<code>

    2、配置連接 Redis 參數

    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    3、配置 Spring 緩存管理器

    緩存配置類,裡面配置緩存管理器,配置緩存的全局過期時間、序列化等參數。

    <code>import org.springframework.cache.CacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.serializer.*;
    import java.time.Duration;

    /**
    * Redis 配置類
    */
    @Configuration
    public class RedisConfig {

    /**
    * 配置緩存管理器
    * @param factory Redis 線程安全連接工廠
    * @return 緩存管理器
    */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
    // 生成兩套默認配置,通過 Config 對象即可對緩存進行自定義配置
    RedisCacheConfiguration cacheConfig1 = RedisCacheConfiguration.defaultCacheConfig()
    // 設置過期時間 10 分鐘
    .entryTtl(Duration.ofMinutes(10))
    // 設置緩存前綴
    .prefixKeysWith("cache:user:")
    // 禁止緩存 null 值
    .disableCachingNullValues()
    // 設置 key 序列化
    .serializeKeysWith(keyPair())
    // 設置 value 序列化
    .serializeValuesWith(valuePair());
    RedisCacheConfiguration cacheConfig2 = RedisCacheConfiguration.defaultCacheConfig()
    // 設置過期時間 30 秒
    .entryTtl(Duration.ofSeconds(30))
    .prefixKeysWith("cache:user_info:")
    .disableCachingNullValues()
    .serializeKeysWith(keyPair())
    .serializeValuesWith(valuePair());

    // 返回 Redis 緩存管理器
    return RedisCacheManager.builder(factory)
    .withCacheConfiguration("user", cacheConfig1)
    .withCacheConfiguration("userInfo", cacheConfig2)
    .build();
    }

    /**
    * 配置鍵序列化
    * @return StringRedisSerializer
    */
    private RedisSerializationContext.SerializationPair<string> keyPair() {
    return RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
    }

    /**
    * 配置值序列化,使用 GenericJackson2JsonRedisSerializer 替換默認序列化
    * @return GenericJackson2JsonRedisSerializer
    */
    private RedisSerializationContext.SerializationPair<object> valuePair() {
    return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    }

    }/<object>/<string>/<code>

    4、定義實體類

    用戶實體類

    User

    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    用戶信息實體類

    UserInfo

    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存

    5、定義服務接口

    UserService

    <code>import mydlq.club.example.entity.User;

    /**

    * 用戶業務接口
    */
    public interface UserService {

    /**
    * 增加賬戶
    *
    * @param user 賬戶
    */
    void addUser(User user);

    /**
    * 獲取賬戶
    *
    * @param username 用戶名
    * @return 用戶信息
    */
    User getUserByUsername(String username);

    /**
    * 修改賬戶
    *
    * @param user 用戶信息
    * @return 用戶信息
    */
    User updateUser(User user);

    /**
    * 刪除賬戶
    * @param username 用戶名
    */
    void deleteByUsername(String username);

    }
    /<code>

    UserInfoService

    <code>import mydlq.club.example.entity.UserInfo;

    /**
    * 用戶信息業務接口
    */

    public interface UserInfoService {

    /**
    * 增加用戶信息
    *
    * @param userInfo 用戶信息
    */
    void addUserInfo(UserInfo userInfo);

    /**
    * 獲取用戶信息
    *
    * @param name 姓名
    * @return 用戶信息
    */
    UserInfo getByName(String name);

    /**
    * 修改用戶信息
    *
    * @param userInfo 用戶信息
    * @return 用戶信息
    */
    UserInfo updateUserInfo(UserInfo userInfo);

    /**
    * 刪除用戶信息
    * @param name 姓名
    */
    void deleteByName(String name);

    }/<code>

    6、實現服務類

    實現 UserService 與 UserInfoService 接口中的方法,裡面使用 @Cacheable、@CachePut、@CacheEvict 三個註解完成對用戶與用戶信息數據的緩存。

    UserServiceImpl(用戶業務實現類)

    注意,為了演示方便,沒有連接數據庫,臨時創建了個成員變量 userMap 來模擬數據庫存儲。

    <code>import mydlq.club.example.entity.User;
    import mydlq.club.example.service.UserService;
    import org.springframework.beans.BeanUtils;
    import org.springframework.cache.annotation.CacheConfig;
    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.HashMap;

    @Service
    @CacheConfig(cacheNames = "user")
    public class UserServiceImpl implements UserService {

    private HashMap<string> userMap = new HashMap<>();

    @Override
    public void addUser(User user) {
    userMap.put(user.getUsername(), user);
    }

    @Override
    @Cacheable(key = "#username",unless = "#result==null ")
    public User getUserByUsername(String username) {
    if (!userMap.containsKey(username)) {
    return null;
    }
    return userMap.get(username);
    }

    @Override
    @CachePut(key = "#user.username")
    public User updateUser(User user) {
    if (!userMap.containsKey(user.getUsername())){
    throw new RuntimeException("不存在該用戶");
    }
    // 獲取存儲的對象
    User newUser = userMap.get(user.getUsername());
    // 複製要更新的數據到新對象,因為不能更改用戶名信息,所以忽略

    BeanUtils.copyProperties(user, newUser, "username");
    // 將新的對象存儲,更新舊對象信息
    userMap.put(newUser.getUsername(), newUser);
    // 返回新對象信息
    return newUser;
    }

    @Override
    @CacheEvict(key = "#username")
    public void deleteByUsername(String username) {
    userMap.remove(username);
    }

    }
    /<string>/<code>

    UserInfoServiceImpl(用戶信息業務實現)

    注意,為了演示方便,沒有連接數據庫,臨時創建了個成員變量 userInfoMap 來模擬數據庫存儲。

    <code>import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.beans.BeanUtils;
    import org.springframework.cache.annotation.CacheConfig;
    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.HashMap;

    @Service
    @CacheConfig(cacheNames = "userInfo")
    public class UserInfoServiceImpl implements UserInfoService {

    private HashMap<string> userInfoMap = new HashMap<>();

    @Override
    public void addUserInfo(UserInfo userInfo) {
    userInfoMap.put(userInfo.getName(), userInfo);
    }

    @Override

    @Cacheable(key = "#name", unless = "#result==null")
    public UserInfo getByName(String name) {
    if (!userInfoMap.containsKey(name)) {
    return null;
    }
    return userInfoMap.get(name);
    }

    @Override
    @CachePut(key = "#userInfo.name")
    public UserInfo updateUserInfo(UserInfo userInfo) {
    if (!userInfoMap.containsKey(userInfo.getName())) {
    throw new RuntimeException("該用戶信息沒有找到");
    }
    // 獲取存儲的對象
    UserInfo newUserInfo = userInfoMap.get(userInfo.getName());
    // 複製要更新的數據到新對象,因為不能更改用戶名信息,所以忽略
    BeanUtils.copyProperties(userInfo, newUserInfo, "name");
    // 將新的對象存儲,更新舊對象信息
    userInfoMap.put(newUserInfo.getName(), newUserInfo);
    // 返回新對象信息
    return newUserInfo;
    }

    @Override
    @CacheEvict(key = "#name")
    public void deleteByName(String name) {
    userInfoMap.remove(name);
    }

    }
    /<string>/<code>

    7、創建 Controller

    UserController

    <code>import mydlq.club.example.entity.User;
    import mydlq.club.example.service.UserService;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    /**
    * 用戶 Controller
    */
    @RestController
    public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/{username}")
    public User getUser(@PathVariable String username) {
    return userService.getUserByUsername(username);
    }

    @PostMapping("/user")
    public String createUser(@RequestBody User user) {
    userService.addUser(user);
    return "SUCCESS";
    }

    @PutMapping("/user")
    public User updateUser(@RequestBody User user) {
    return userService.updateUser(user);
    }

    @DeleteMapping("/user/{username}")
    public String deleteUser(@PathVariable String username) {
    userService.deleteByUsername(username);
    return "SUCCESS";
    }

    }
    /<code>

    UserInfoController

    <code>import mydlq.club.example.entity.UserInfo;
    import mydlq.club.example.service.UserInfoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    /**
    * 用戶信息 Controller
    */
    @RestController

    public class UserInfoController {

    @Autowired
    private UserInfoService userInfoService;

    @GetMapping("/userInfo/{name}")
    public UserInfo getUserInfo(@PathVariable String name) {
    return userInfoService.getByName(name);
    }

    @PostMapping("/userInfo")
    public String createUserInfo(@RequestBody UserInfo userInfo) {
    userInfoService.addUserInfo(userInfo);
    return "SUCCESS";
    }

    @PutMapping("/userInfo")
    public UserInfo updateUserInfo(@RequestBody UserInfo userInfo) {
    return userInfoService.updateUserInfo(userInfo);
    }

    @DeleteMapping("/userInfo/{name}")
    public String deleteUserInfo(@PathVariable String name) {
    userInfoService.deleteByName(name);
    return "SUCCESS";
    }

    }
    /<code>

    8、啟動類

    啟動類中添加 @EnableCaching 註解開啟緩存。

    SpringBoot 結合 Spring Cache 操作 Redis 實現數據緩存


    分享到:


    相關文章: