
作者:超级小豆丁来源:http://www.mydlq.club/article/56/
环境配置:
缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。
之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好地作为分布式缓存组件提供多个服务间的缓存,但是 Redis 这种还是需要网络开销,增加时耗。
本地缓存是直接从本地内存中读取,没有网络开销,例如秒杀系统或者数据量小的缓存等,比远程缓存更合适。
按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。
并且在 Spring5 (Spring Boot 2.x) 后,Spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。
可以通过下图观测到,在下面缓存组件中 Caffeine 性能是其中最好的。


注意:
- // 软引用
- Caffeine.newBuilder().softValues().build();
-
- // 弱引用
- Caffeine.newBuilder().weakKeys().weakValues().build();
SpringBoot 有两种使用 Caffeine 作为缓存的方式:
方式一: 直接引入 Caffeine 依赖,然后使用 Caffeine 方法实现缓存。
方式二: 引入 Caffeine 和 Spring Cache 依赖,使用 SpringCache 注解方法实现缓存。
关注公众号Java技术栈,回复:面试,可以获取我整理的 Spring Boot 系列面试题和答案。下面将介绍下,这两种集成方式都是如何实现的。
Spring Boot 基础就不介绍了,推荐看下这个教程:
https://github.com/javastacks/spring-boot-best-practice
- <?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>
- 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();
- }
-
- }
- import lombok.Data;
- import lombok.ToString;
-
- @Data
- @ToString
- public class UserInfo {
- private Integer id;
- private String name;
- private String sex;
- private Integer age;
- }
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));
- }
-
- }
- 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";
- }
-
- }
- <?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>
- @Configuration
- public 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;
- }
-
- }
- @Data
- @ToString
- public class UserInfo {
- private Integer id;
- private String name;
- private String sex;
- private Integer age;
- }
服务接口:
- 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<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);
- }
-
- }
- 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";
- }
-
- }