環境配置:
JDK 版本:1.8
Caffeine 版本:2.8.0
SpringBoot 版本:2.2.2.RELEASE
一、本地緩存介紹
緩存在日常開發中啟動至關重要的作用,由于是存儲在記憶體中,資料的讀取速度是非常快的,能大量減少對資料庫的通路,減少資料庫的壓力。
之前介紹過 Redis 這種 NoSql 作為緩存元件,它能夠很好的作為分布式緩存元件提供多個服務間的緩存,但是 Redis 這種還是需要網絡開銷,增加時耗。本地緩存是直接從本地記憶體中讀取,沒有網絡開銷,例如秒殺系統或者資料量小的緩存等,比遠端緩存更合适。
二、緩存元件 Caffeine 介紹
按 Caffeine Github 文檔描述,Caffeine 是基于 JAVA 8 的高性能緩存庫。并且在 spring5 (springboot 2.x) 後,spring 官方放棄了 Guava,而使用了性能更優秀的 Caffeine 作為預設緩存元件。
1、Caffeine 性能
可以通過下圖觀測到,在下面緩存元件中 Caffeine 性能是其中最好的。

注意:
weakValues 和 softValues 不可以同時使用。
maximumSize 和 maximumWeight 不可以同時使用。
expireAfterWrite 和 expireAfterAccess 同僚存在時,以 expireAfterWrite 為準。
3、軟引用與弱引用
軟引用: 如果一個對象隻具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些對象的記憶體。
弱引用: 弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的記憶體區域的過程中,一旦發現了隻具有弱引用的對象,不管目前記憶體空間足夠與否,都會回收它的記憶體
三、SpringBoot 內建 Caffeine 兩種方式
SpringBoot 有倆種使用 Caffeine 作為緩存的方式:
方式一: 直接引入 Caffeine 依賴,然後使用 Caffeine 方法實作緩存。
方式二: 引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 注解方法實作緩存。
下面将介紹下,這倆中內建方式都是如何實作的。
Spring Boot 基礎就不介紹了,推薦看下這個教程:
https://github.com/javastacks/spring-boot-best-practice四、SpringBoot 內建 Caffeine 方式一
1、Maven 引入相關依賴
<?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()
// 設定最後一次寫入或通路後經過固定時間過期
.expireAfterWrite(60, TimeUnit.SECONDS)
// 初始的緩存空間大小
.initialCapacity(100)
// 緩存的最大條數
.maximumSize(1000)
.build();
}
}
3、定義測試的實體對象
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public 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
@Service
public class UserInfoServiceImpl implements UserInfoService {
/**
* 模拟資料庫存儲資料
*/
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());
// 替換内容
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));
}
}
五、SpringBoot 內建 Caffeine 方式二
<?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>
服務實作類
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<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());
// 替換内容
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
@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近期熱文推薦:
1.Java 15 正式釋出, 14 個新特性,重新整理你的認知!!
2.終于靠開源項目弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同僚直呼看不懂,你試試看。。
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新釋出,速速下載下傳!
覺得不錯,别忘了随手點贊+轉發哦!