SpringBoot 使用 Caffeine 本地快取

目錄

一、本地快取介紹

二、快取元件 Caffeine 介紹

Caffeine 效能

Caffeine 配置說明

軟引用與弱引用

三、SpringBoot 整合 Caffeine 兩種方式

四、SpringBoot 整合 Caffeine 方式一

Maven 引入相關依賴

配置快取配置類

定義測試的實體物件

定義服務介面類和實現類

測試的 Controller 類

五、SpringBoot 整合 Caffeine 方式二

Maven 引入相關依賴

配置快取配置類

定義測試的實體物件

定義服務介面類和實現類

測試的 Controller 類

環境配置:

JDK 版本:1。8

Caffeine 版本:2。8。0

SpringBoot 版本:2。2。2。RELEASE

參考地址:

https://www。jianshu。com/p/c72fb0c787fc

https://www。cnblogs。com/rickiyang/p/11074158。html

博文示例專案 Github 地址:https://github。com/my-dlq/blog-example/tree/master/springboot/springboot-caffeine-cache-example

一、本地快取介紹

快取在日常開發中啟動至關重要的作用,由於是儲存在記憶體中,資料的讀取速度是非常快的,能大量減少對資料庫的訪問,減少資料庫的壓力。

之前介紹過 Redis 這種 NoSql 作為快取元件,它能夠很好的作為分散式快取元件提供多個服務間的快取,但是 Redis 這種還是需要網路開銷,增加時耗。本地快取是直接從本地記憶體中讀取,沒有網路開銷,例如秒殺系統或者資料量小的快取等,比遠端快取更合適。

二、快取元件 Caffeine 介紹

按 Caffeine Github 文件描述,Caffeine 是基於 JAVA 8 的高效能快取庫。並且在 spring5 (springboot 2。x) 後,spring 官方放棄了 Guava,而使用了效能更優秀的 Caffeine 作為預設快取元件。

1、Caffeine 效能

可以透過下圖觀測到,在下面快取元件中 Caffeine 效能是其中最好的。

SpringBoot 使用 Caffeine 本地快取

2、Caffeine 配置說明

SpringBoot 使用 Caffeine 本地快取

注意:

weakValues 和 softValues 不可以同時使用。

maximumSize 和 maximumWeight 不可以同時使用。

expireAfterWrite 和 expireAfterAccess 同事存在時,以 expireAfterWrite 為準。

3、軟引用與弱引用

軟引用:如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。

弱引用:弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體

// 軟引用Caffeine。newBuilder()。softValues()。build();// 弱引用Caffeine。newBuilder()。weakKeys()。weakValues()。build();

三、SpringBoot 整合 Caffeine 兩種方式

SpringBoot 有倆種使用 Caffeine 作為快取的方式:

方式一:直接引入 Caffeine 依賴,然後使用 Caffeine 方法實現快取。

方式二:引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 註解方法實現快取。

下面將介紹下,這倆中整合方式都是如何實現的。

四、SpringBoot 整合 Caffeine 方式一

1、Maven 引入相關依賴

<?xml version=“1。0” encoding=“UTF-8”?> 4。0。0 org。springframework。boot spring-boot-starter-parent 2。2。2。RELEASE mydlq。club springboot-caffeine-cache-example-1 0。0。1 springboot-caffeine-cache-example-1 Demo project for Spring Boot Cache 1。8 org。springframework。boot spring-boot-starter-web com。github。ben-manes。caffeine caffeine org。projectlombok lombok org。springframework。boot spring-boot-maven-plugin

2、配置快取配置類

import com。github。benmanes。caffeine。cache。Cache;import com。github。benmanes。caffeine。cache。Caffeine;import org。springframework。context。annotation。Bean;import org。springframework。context。annotation。Configuration;import java。util。concurrent。TimeUnit;@Configurationpublic class CacheConfig { @Bean public Cache caffeineCache() { return Caffeine。newBuilder() // 設定最後一次寫入或訪問後經過固定時間過期 。expireAfterWrite(60, TimeUnit。SECONDS) // 初始的快取空間大小 。initialCapacity(100) // 快取的最大條數 。maximumSize(1000) 。build(); }}

3、定義測試的實體物件

import lombok。Data;import lombok。ToString;@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}

4、定義服務介面類和實現類

UserInfoService

import mydlq。club。example。entity。UserInfo;public interface UserInfoService { /** * 增加使用者資訊 * * @param userInfo 使用者資訊 */ void addUserInfo(UserInfo userInfo); /** * 獲取使用者資訊 * * @param id 使用者ID * @return 使用者資訊 */ UserInfo getByName(Integer id); /** * 修改使用者資訊 * * @param userInfo 使用者資訊 * @return 使用者資訊 */ UserInfo updateUserInfo(UserInfo userInfo); /** * 刪除使用者資訊 * * @param id 使用者ID */ void deleteById(Integer id);}

UserInfoServiceImpl

import com。github。benmanes。caffeine。cache。Cache;import lombok。extern。slf4j。Slf4j;import mydlq。club。example。entity。UserInfo;import mydlq。club。example。service。UserInfoService;import org。springframework。beans。factory。annotation。Autowired;import org。springframework。stereotype。Service;import org。springframework。util。StringUtils;import java。util。HashMap;@Slf4j@Servicepublic class UserInfoServiceImpl implements UserInfoService { /** * 模擬資料庫儲存資料 */ private HashMap userInfoMap = new HashMap<>(); @Autowired Cache caffeineCache; @Override public void addUserInfo(UserInfo userInfo) { log。info(“create”); userInfoMap。put(userInfo。getId(), userInfo); // 加入快取 caffeineCache。put(String。valueOf(userInfo。getId()),userInfo); } @Override public UserInfo getByName(Integer id) { // 先從快取讀取 caffeineCache。getIfPresent(id); UserInfo userInfo = (UserInfo) caffeineCache。asMap()。get(String。valueOf(id)); if (userInfo != null){ return userInfo; } // 如果快取中不存在,則從庫中查詢 log。info(“get”); userInfo = userInfoMap。get(id); // 如果使用者資訊不為空,則加入快取 if (userInfo != null){ caffeineCache。put(String。valueOf(userInfo。getId()),userInfo); } return userInfo; } @Override public UserInfo updateUserInfo(UserInfo userInfo) { log。info(“update”); if (!userInfoMap。containsKey(userInfo。getId())) { return null; } // 取舊的值 UserInfo oldUserInfo = userInfoMap。get(userInfo。getId()); // 替換內容 if (!StringUtils。isEmpty(oldUserInfo。getAge())) { oldUserInfo。setAge(userInfo。getAge()); } if (!StringUtils。isEmpty(oldUserInfo。getName())) { oldUserInfo。setName(userInfo。getName()); } if (!StringUtils。isEmpty(oldUserInfo。getSex())) { oldUserInfo。setSex(userInfo。getSex()); } // 將新的物件儲存,更新舊物件資訊 userInfoMap。put(oldUserInfo。getId(), oldUserInfo); // 替換快取中的值 caffeineCache。put(String。valueOf(oldUserInfo。getId()),oldUserInfo); return oldUserInfo; } @Override public void deleteById(Integer id) { log。info(“delete”); userInfoMap。remove(id); // 從快取中刪除 caffeineCache。asMap()。remove(String。valueOf(id)); }}

5、測試的 Controller 類

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。*;@RestController@RequestMappingpublic class UserInfoController { @Autowired private UserInfoService userInfoService; @GetMapping(“/userInfo/{id}”) public Object getUserInfo(@PathVariable Integer id) { UserInfo userInfo = userInfoService。getByName(id); if (userInfo == null) { return “沒有該使用者”; } return userInfo; } @PostMapping(“/userInfo”) public Object createUserInfo(@RequestBody UserInfo userInfo) { userInfoService。addUserInfo(userInfo); return “SUCCESS”; } @PutMapping(“/userInfo”) public Object updateUserInfo(@RequestBody UserInfo userInfo) { UserInfo newUserInfo = userInfoService。updateUserInfo(userInfo); if (newUserInfo == null){ return “不存在該使用者”; } return newUserInfo; } @DeleteMapping(“/userInfo/{id}”) public Object deleteUserInfo(@PathVariable Integer id) { userInfoService。deleteById(id); return “SUCCESS”; }}

五、SpringBoot 整合 Caffeine 方式二

1、Maven 引入相關依賴

<?xml version=“1。0” encoding=“UTF-8”?> 4。0。0 org。springframework。boot spring-boot-starter-parent 2。2。2。RELEASE mydlq。club springboot-caffeine-cache-example-2 0。0。1 springboot-caffeine-cache-example-2 Demo project for Spring Boot caffeine 1。8 org。springframework。boot spring-boot-starter-web org。springframework。boot spring-boot-starter-cache com。github。ben-manes。caffeine caffeine org。projectlombok lombok org。springframework。boot spring-boot-maven-plugin

2、配置快取配置類

@Configurationpublic class CacheConfig { /** * 配置快取管理器 * * @return 快取管理器 */ @Bean(“caffeineCacheManager”) public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager。setCaffeine(Caffeine。newBuilder() // 設定最後一次寫入或訪問後經過固定時間過期 。expireAfterAccess(60, TimeUnit。SECONDS) // 初始的快取空間大小 。initialCapacity(100) // 快取的最大條數 。maximumSize(1000)); return cacheManager; }}

3、定義測試的實體物件

@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}

4、定義服務介面類和實現類

服務介面

import mydlq。club。example。entity。UserInfo;public interface UserInfoService { /** * 增加使用者資訊 * * @param userInfo 使用者資訊 */ void addUserInfo(UserInfo userInfo); /** * 獲取使用者資訊 * * @param id 使用者ID * @return 使用者資訊 */ UserInfo getByName(Integer id); /** * 修改使用者資訊 * * @param userInfo 使用者資訊 * @return 使用者資訊 */ UserInfo updateUserInfo(UserInfo userInfo); /** * 刪除使用者資訊 * * @param id 使用者ID */ void deleteById(Integer id);}

服務實現類

import lombok。extern。slf4j。Slf4j;import mydlq。club。example。entity。UserInfo;import mydlq。club。example。service。UserInfoService;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 org。springframework。util。StringUtils;import java。util。HashMap;@Slf4j@Service@CacheConfig(cacheNames = “caffeineCacheManager”)public class UserInfoServiceImpl implements UserInfoService { /** * 模擬資料庫儲存資料 */ private HashMap userInfoMap = new HashMap<>(); @Override @CachePut(key = “#userInfo。id”) public void addUserInfo(UserInfo userInfo) { log。info(“create”); userInfoMap。put(userInfo。getId(), userInfo); } @Override @Cacheable(key = “#id”) public UserInfo getByName(Integer id) { log。info(“get”); return userInfoMap。get(id); } @Override @CachePut(key = “#userInfo。id”) public UserInfo updateUserInfo(UserInfo userInfo) { log。info(“update”); if (!userInfoMap。containsKey(userInfo。getId())) { return null; } // 取舊的值 UserInfo oldUserInfo = userInfoMap。get(userInfo。getId()); // 替換內容 if (!StringUtils。isEmpty(oldUserInfo。getAge())) { oldUserInfo。setAge(userInfo。getAge()); } if (!StringUtils。isEmpty(oldUserInfo。getName())) { oldUserInfo。setName(userInfo。getName()); } if (!StringUtils。isEmpty(oldUserInfo。getSex())) { oldUserInfo。setSex(userInfo。getSex()); } // 將新的物件儲存,更新舊物件資訊 userInfoMap。put(oldUserInfo。getId(), oldUserInfo); // 返回新物件資訊 return oldUserInfo; } @Override @CacheEvict(key = “#id”) public void deleteById(Integer id) { log。info(“delete”); userInfoMap。remove(id); }}

5、測試的 Controller 類

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。*;@RestController@RequestMappingpublic class UserInfoController { @Autowired private UserInfoService userInfoService; @GetMapping(“/userInfo/{id}”) public Object getUserInfo(@PathVariable Integer id) { UserInfo userInfo = userInfoService。getByName(id); if (userInfo == null) { return “沒有該使用者”; } return userInfo; } @PostMapping(“/userInfo”) public Object createUserInfo(@RequestBody UserInfo userInfo) { userInfoService。addUserInfo(userInfo); return “SUCCESS”; } @PutMapping(“/userInfo”) public Object updateUserInfo(@RequestBody UserInfo userInfo) { UserInfo newUserInfo = userInfoService。updateUserInfo(userInfo); if (newUserInfo == null){ return “不存在該使用者”; } return newUserInfo; } @DeleteMapping(“/userInfo/{id}”) public Object deleteUserInfo(@PathVariable Integer id) { userInfoService。deleteById(id); return “SUCCESS”; }}