Spring Boot 2.x 把 Guava 干掉了選擇本地緩存之王 Caffeine(推薦)
環(huán)境配置:
- JDK 版本:1.8
- Caffeine 版本:2.8.0
- SpringBoot 版本:2.2.2.RELEASE
一、本地緩存介紹
緩存在日常開發(fā)中啟動(dòng)至關(guān)重要的作用,由于是存儲(chǔ)在內(nèi)存中,數(shù)據(jù)的讀取速度是非??斓?,能大量減少對(duì)數(shù)據(jù)庫的訪問,減少數(shù)據(jù)庫的壓力。
之前介紹過 Redis 這種 NoSql 作為緩存組件,它能夠很好的作為分布式緩存組件提供多個(gè)服務(wù)間的緩存,但是 Redis 這種還是需要網(wǎng)絡(luò)開銷,增加時(shí)耗。本地緩存是直接從本地內(nèi)存中讀取,沒有網(wǎng)絡(luò)開銷,例如秒殺系統(tǒng)或者數(shù)據(jù)量小的緩存等,比遠(yuǎn)程緩存更合適。
二、緩存組件 Caffeine 介紹
按 Caffeine Github 文檔描述,Caffeine 是基于 JAVA 8 的高性能緩存庫。并且在 spring5 (springboot 2.x) 后,spring 官方放棄了 Guava,而使用了性能更優(yōu)秀的 Caffeine 作為默認(rèn)緩存組件。
1、Caffeine 性能
可以通過下圖觀測到,在下面緩存組件中 Caffeine 性能是其中最好的。

2、Caffeine 配置說明
| 參數(shù) | 類型 | 描述 |
|---|---|---|
| initialCapacity | integer | 初始的緩存空間大小 |
| maximumSize | long | 緩存的最大條數(shù) |
| maximumWeight | long | 緩存的最大權(quán)重 |
| expireAfterAccess | duration | 最后一次寫入或訪問后經(jīng)過固定時(shí)間過期 |
| refreshAfterWrite | duration | 最后一次寫入后經(jīng)過固定時(shí)間過期 |
| refreshAfterWrite | duration | 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過固定的時(shí)間間隔,刷新緩存 |
| weakKeys | boolean | 打開 key 的弱引用 |
| weakValues | boolean | 打開 value 的弱引用 |
| softValues | boolean | 打開 value 的軟引用 |
| recordStats | - | 開發(fā)統(tǒng)計(jì)功能 |
注意:
weakValues和softValues不可以同時(shí)使用。maximumSize和maximumWeight不可以同時(shí)使用。expireAfterWrite和expireAfterAccess同事存在時(shí),以expireAfterWrite為準(zhǔn)。
3、軟引用與弱引用
- 軟引用: 如果一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存。
- 弱引用: 弱引用的對(duì)象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存
// 軟引用 Caffeine.newBuilder().softValues().build(); // 弱引用 Caffeine.newBuilder().weakKeys().weakValues().build();
三、SpringBoot 集成 Caffeine 兩種方式
SpringBoot 有倆種使用 Caffeine 作為緩存的方式:
方式一: 直接引入 Caffeine 依賴,然后使用 Caffeine 方法實(shí)現(xiàn)緩存。
方式二: 引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 注解方法實(shí)現(xiàn)緩存。
下面將介紹下,這倆中集成方式都是如何實(shí)現(xiàn)的。
Spring Boot 基礎(chǔ)就不介紹了,推薦看下這個(gè)教程:
https://github.com/javastacks/spring-boot-best-practice
四、SpringBoot 集成Caffeine 方式一
1、Maven 引入相關(guān)依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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-caffeine-cache-example-1</artifactId> <version>0.0.1</version> <name>springboot-caffeine-cache-example-1</name> <description>Demo project for Spring Boot Cache</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>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</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>
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;
@Configuration
public class CacheConfig {
@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
// 設(shè)置最后一次寫入或訪問后經(jīng)過固定時(shí)間過期
.expireAfterWrite(60, TimeUnit.SECONDS)
// 初始的緩存空間大小
.initialCapacity(100)
// 緩存的最大條數(shù)
.maximumSize(1000)
.build();
}
}
3、定義測試的實(shí)體對(duì)象
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class UserInfo {
private Integer id;
private String name;
private String sex;
private Integer age;
}
4、定義服務(wù)接口類和實(shí)現(xiàn)類
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
@Service
public class UserInfoServiceImpl implements UserInfoService {
/**
* 模擬數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)
*/
private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
@Autowired
Cache<String, Object> 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());
// 替換內(nèi)容
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());
}
// 將新的對(duì)象存儲(chǔ),更新舊對(duì)象信息
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
@RequestMapping
public 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 引入相關(guān)依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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-caffeine-cache-example-2</artifactId> <version>0.0.1</version> <name>springboot-caffeine-cache-example-2</name> <description>Demo project for Spring Boot caffeine</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-cache</artifactId> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</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>
2、配置緩存配置類
@Configuration
public class CacheConfig {
/**
* 配置緩存管理器
*
* @return 緩存管理器
*/
@Bean("caffeineCacheManager")
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
// 設(shè)置最后一次寫入或訪問后經(jīng)過固定時(shí)間過期
.expireAfterAccess(60, TimeUnit.SECONDS)
// 初始的緩存空間大小
.initialCapacity(100)
// 緩存的最大條數(shù)
.maximumSize(1000));
return cacheManager;
}
}
3、定義測試的實(shí)體對(duì)象
@Data
@ToString
public class UserInfo {
private Integer id;
private String name;
private String sex;
private Integer age;
}
4、定義服務(wù)接口類和實(shí)現(xiàn)類
服務(wù)接口
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);
}
服務(wù)實(shí)現(xiàn)類
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 {
/**
* 模擬數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)
*/
private HashMap<Integer, UserInfo> 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());
// 替換內(nèi)容
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());
}
// 將新的對(duì)象存儲(chǔ),更新舊對(duì)象信息
userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
// 返回新對(duì)象信息
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
@RequestMapping
public 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";
}
}
參考地址:
https://www.jianshu.com/p/c72fb0c787fc
https://www.cnblogs.com/rickiyang/p/11074158.html
https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-caffeine-cache-example
到此這篇關(guān)于Spring Boot 2.x 把 Guava 干掉了選擇本地緩存之王 Caffeine的文章就介紹到這了,更多相關(guān)Spring Boot 2.x Caffeine內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JVM 方法調(diào)用之動(dòng)態(tài)分派(詳解)
下面小編就為大家?guī)硪黄狫VM 方法調(diào)用之動(dòng)態(tài)分派(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
java實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Java利用Phantomjs實(shí)現(xiàn)生成圖片的功能
這篇文章主要介紹了Java利用Phantomjs實(shí)現(xiàn)生成圖片的功能,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-08-08
mybatis項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)表名的三種方法
有時(shí)在開發(fā)過程中java代碼中的表名和數(shù)據(jù)庫的表名并不是一致的,此時(shí)我們就需要?jiǎng)討B(tài)的設(shè)置表名,本文主要介紹了mybatis項(xiàng)目實(shí)現(xiàn)動(dòng)態(tài)表名的三種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
springboot3.4和mybatis plus的版本問題的解決
本文主要介紹了springboot3.4和mybatis plus的版本問題的解決,主要由于Spring Boot 3.4與MyBatis-Plus版本不匹配導(dǎo)致分頁功能問題,下面就來解決這個(gè)問題,感興趣的可以了解一下2025-03-03
淺談springMVC接收前端json數(shù)據(jù)的總結(jié)
下面小編就為大家分享一篇淺談springMVC接收前端json數(shù)據(jù)的總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-03-03
IntelliJ IDEA設(shè)置代碼的快捷編輯模板Live Templates
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA設(shè)置代碼的快捷編輯模板Live Templates,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10

