目錄
一、本地緩存介紹
二、緩存組件 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
一、本地緩存介紹
緩存在日常開發中啟動至關重要的作用,由於是存儲在內存中,數據的讀取速度是非常快的,能大量減少對數據庫的訪問,減少數據庫的壓力。
之前介紹過 Redis 這種 NoSql 作為緩存組件,它能夠很好的作為分佈式緩存組件提供多個服務間的緩存,但是 Redis 這種還是需要網絡開銷,增加時耗。本地緩存是直接從本地內存中讀取,沒有網絡開銷,例如秒殺系統或者數據量小的緩存等,比遠程緩存更合適。
二、緩存組件 Caffeine 介紹
按 Caffeine Github 文檔描述,Caffeine 是基於 JAVA 8 的高性能緩存庫。並且在 spring5 (springboot 2.x) 後,spring 官方放棄了 Guava,而使用了性能更優秀的 Caffeine 作為默認緩存組件。
1、Caffeine 性能
可以通過下圖觀測到,在下面緩存組件中 Caffeine 性能是其中最好的。
2、Caffeine 配置說明
注意:
- weakValues 和 softValues 不可以同時使用。
- maximumSize 和 maximumWeight 不可以同時使用。
- expireAfterWrite 和 expireAfterAccess 同事存在時,以 expireAfterWrite 為準。
3、軟引用與弱引用
軟引用:如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。
弱引用:弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存
<code>// 軟引用Caffeine.newBuilder().softValues().build();// 弱引用Caffeine.newBuilder().weakKeys().weakValues().build();/<code>
三、SpringBoot 集成 Caffeine 兩種方式
SpringBoot 有倆種使用 Caffeine 作為緩存的方式:
- 方式一:直接引入 Caffeine 依賴,然後使用 Caffeine 方法實現緩存。
- 方式二:引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 註解方法實現緩存。
下面將介紹下,這倆中集成方式都是如何實現的。
四、SpringBoot 集成 Caffeine 方式一
1、Maven 引入相關依賴
<code><project> <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>/<code>
2、配置緩存配置類
<code>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> caffeineCache() { return Caffeine.newBuilder() // 設置最後一次寫入或訪問後經過固定時間過期 .expireAfterWrite(60, TimeUnit.SECONDS) // 初始的緩存空間大小 .initialCapacity(100) // 緩存的最大條數 .maximumSize(1000) .build(); }}/<string>/<code>
3、定義測試的實體對象
<code>import lombok.Data;import lombok.ToString;@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}/<code>
4、定義服務接口類和實現類
UserInfoService
<code>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);}/<code>
UserInfoServiceImpl
<code>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<integer> userInfoMap = new HashMap<>(); @Autowired Cache<string> 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)); }}/<string>/<integer>/<code>
5、測試的 Controller 類
<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.*;@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"; }}/<code>
五、SpringBoot 集成 Caffeine 方式二
1、Maven 引入相關依賴
<code><project> <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>/<code>
2、配置緩存配置類
<code>@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; }}/<code>
3、定義測試的實體對象
<code>@Data@ToStringpublic class UserInfo { private Integer id; private String name; private String sex; private Integer age;}/<code>
4、定義服務接口類和實現類
服務接口
<code>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);}/<code>
服務實現類
<code>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> 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); }}/<integer>/<code>
5、測試的 Controller 類
<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.*;@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"; }}/<code>
END
閱讀更多 Java技術虎 的文章