Spring Boot 2.x 把 Guava 干掉了選擇本地緩存之王 Caffeine(推薦)
環(huán)境配置:
JDK 版本:1.8 Caffeine 版本:2.8.0 SpringBoot 版本:2.2.2.RELEASE一、本地緩存介紹緩存在日常開(kāi)發(fā)中啟動(dòng)至關(guān)重要的作用,由于是存儲(chǔ)在內(nèi)存中,數(shù)據(jù)的讀取速度是非常快的,能大量減少對(duì)數(shù)據(jù)庫(kù)的訪問(wèn),減少數(shù)據(jù)庫(kù)的壓力。
之前介紹過(guò) Redis 這種 NoSql 作為緩存組件,它能夠很好的作為分布式緩存組件提供多個(gè)服務(wù)間的緩存,但是 Redis 這種還是需要網(wǎng)絡(luò)開(kāi)銷(xiāo),增加時(shí)耗。本地緩存是直接從本地內(nèi)存中讀取,沒(méi)有網(wǎng)絡(luò)開(kāi)銷(xiāo),例如秒殺系統(tǒng)或者數(shù)據(jù)量小的緩存等,比遠(yuǎn)程緩存更合適。
二、緩存組件 Caffeine 介紹按 Caffeine Github 文檔描述,Caffeine 是基于 JAVA 8 的高性能緩存庫(kù)。并且在 spring5 (springboot 2.x) 后,spring 官方放棄了 Guava,而使用了性能更優(yōu)秀的 Caffeine 作為默認(rèn)緩存組件。
1、Caffeine 性能可以通過(guò)下圖觀測(cè)到,在下面緩存組件中 Caffeine 性能是其中最好的。
參數(shù) 類(lèi)型 描述 initialCapacity integer 初始的緩存空間大小 maximumSize long 緩存的最大條數(shù) maximumWeight long 緩存的最大權(quán)重 expireAfterAccess duration 最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期 refreshAfterWrite duration 最后一次寫(xiě)入后經(jīng)過(guò)固定時(shí)間過(guò)期 refreshAfterWrite duration 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過(guò)固定的時(shí)間間隔,刷新緩存 weakKeys boolean 打開(kāi) key 的弱引用 weakValues boolean 打開(kāi) value 的弱引用 softValues boolean 打開(kāi) value 的軟引用 recordStats - 開(kāi)發(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ū)域的過(guò)程中,一旦發(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 依賴(lài),然后使用 Caffeine 方法實(shí)現(xiàn)緩存。
方式二: 引入 Caffeine 和 Spring Cache 依賴(lài),使用 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)依賴(lài)
<?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、配置緩存配置類(lèi)
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<String, Object> caffeineCache() { return Caffeine.newBuilder() // 設(shè)置最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期 .expireAfterWrite(60, TimeUnit.SECONDS) // 初始的緩存空間大小 .initialCapacity(100) // 緩存的最大條數(shù) .maximumSize(1000) .build(); }}
3、定義測(cè)試的實(shí)體對(duì)象
import lombok.Data;import lombok.ToString;@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}
4、定義服務(wù)接口類(lèi)和實(shí)現(xiàn)類(lèi)
UserInfoService
import mydlq.club.example.entity.UserInfo;public interface UserInfoService { /** * 增加用戶(hù)信息 * * @param userInfo 用戶(hù)信息 */ void addUserInfo(UserInfo userInfo); /** * 獲取用戶(hù)信息 * * @param id 用戶(hù)ID * @return 用戶(hù)信息 */ UserInfo getByName(Integer id); /** * 修改用戶(hù)信息 * * @param userInfo 用戶(hù)信息 * @return 用戶(hù)信息 */ UserInfo updateUserInfo(UserInfo userInfo); /** * 刪除用戶(hù)信息 * * @param id 用戶(hù)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 { /** * 模擬數(shù)據(jù)庫(kù)存儲(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; } // 如果緩存中不存在,則從庫(kù)中查找 log.info('get'); userInfo = userInfoMap.get(id); // 如果用戶(hù)信息不為空,則加入緩存 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、測(cè)試的 Controller 類(lèi)
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 '沒(méi)有該用戶(hù)'; } 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 '不存在該用戶(hù)'; } return newUserInfo; } @DeleteMapping('/userInfo/{id}') public Object deleteUserInfo(@PathVariable Integer id) { userInfoService.deleteById(id); return 'SUCCESS'; }}五、SpringBoot 集成 Caffeine 方式二
1、Maven 引入相關(guān)依賴(lài)
<?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、配置緩存配置類(lèi)
@Configurationpublic class CacheConfig { /** * 配置緩存管理器 * * @return 緩存管理器 */ @Bean('caffeineCacheManager') public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.newBuilder() // 設(shè)置最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期 .expireAfterAccess(60, TimeUnit.SECONDS) // 初始的緩存空間大小 .initialCapacity(100) // 緩存的最大條數(shù) .maximumSize(1000)); return cacheManager; }}
3、定義測(cè)試的實(shí)體對(duì)象
@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}
4、定義服務(wù)接口類(lèi)和實(shí)現(xiàn)類(lèi)
服務(wù)接口
import mydlq.club.example.entity.UserInfo;public interface UserInfoService { /** * 增加用戶(hù)信息 * * @param userInfo 用戶(hù)信息 */ void addUserInfo(UserInfo userInfo); /** * 獲取用戶(hù)信息 * * @param id 用戶(hù)ID * @return 用戶(hù)信息 */ UserInfo getByName(Integer id); /** * 修改用戶(hù)信息 * * @param userInfo 用戶(hù)信息 * @return 用戶(hù)信息 */ UserInfo updateUserInfo(UserInfo userInfo); /** * 刪除用戶(hù)信息 * * @param id 用戶(hù)ID */ void deleteById(Integer id);}
服務(wù)實(shí)現(xiàn)類(lèi)
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ù)庫(kù)存儲(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、測(cè)試的 Controller 類(lèi)
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 '沒(méi)有該用戶(hù)'; } 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 '不存在該用戶(hù)'; } return newUserInfo; } @DeleteMapping('/userInfo/{id}') public Object deleteUserInfo(@PathVariable Integer id) { userInfoService.deleteById(id); return 'SUCCESS'; }}
參考地址:
https://www.jianshu.com/p/c72fb0c787fchttps://www.cnblogs.com/rickiyang/p/11074158.htmlhttps://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)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. asp(vbs)Rs.Open和Conn.Execute的詳解和區(qū)別及&H0001的說(shuō)明2. CSS hack用法案例詳解3. ASP 處理JSON數(shù)據(jù)的實(shí)現(xiàn)代碼4. PHP設(shè)計(jì)模式中工廠模式深入詳解5. 用css截取字符的幾種方法詳解(css排版隱藏溢出文本)6. asp中response.write("中文")或者js中文亂碼問(wèn)題7. 將properties文件的配置設(shè)置為整個(gè)Web應(yīng)用的全局變量實(shí)現(xiàn)方法8. ThinkPHP5實(shí)現(xiàn)JWT Token認(rèn)證的過(guò)程(親測(cè)可用)9. ASP 信息提示函數(shù)并作返回或者轉(zhuǎn)向10. jsp網(wǎng)頁(yè)實(shí)現(xiàn)貪吃蛇小游戲
