🧃SpringBoot整合Caffeine(注解方式)
一、了解缓存配置
先来了解一下配置方法吧,SimpleCacheManager和CaffeineCacheManager配置的区别:
SimpleCacheManager: 这种缓存管理器允许你在应用程序启动时通过配置多个CaffeineCache来创建多个缓存。 这种方式可以让你为每个方法单独配置缓存过期时间。 CaffeineCacheManager: 这种缓存管理器使用了一个全局的Caffeine配置来创建所有的缓存。 这种方式不能为每个方法单独配置缓存过期时间,但是可以在程序启动时配置全局的缓存配置,这样就可以轻松地设置所有方法的缓存过期时间。 总结: 如果你希望为每个方法单独配置缓存过期时间,那么建议使用第一种方式。 否则,如果你希望设置全局的缓存配置,那么建议使用第二种方式。
本文使用MP进行构建,Gitee地址:https://gitee.com/zhang-zhixi/springboot-caffeine.git
二、使用缓存:SimpleCacheManager方式
实体类:User
package com.zhixi.pojo; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.time.LocalDateTime; /** * @TableName user */ @TableName(value = "user" ) @Data public class User implements Serializable { /** * 主键ID */ @TableId(value = "id" , type = IdType.AUTO) private Long id; /** * 用户名 */ @TableField(value = "username" ) private String username; /** * 密码 */ @TableField(value = "password" ) private String password; /** * 姓名 */ @TableField(value = "name" ) private String name; /** * 年龄 */ @TableField(value = "age" ) private Integer age; /** * 邮箱 */ @TableField(value = "email" ) private String email; /** * 版本号,用于乐观锁 */ @TableField(value = "version" ) private Integer version; /** * 创建时间 */ @TableField(value = "create_time" , fill = FieldFill.INSERT) @JsonDeserialize( using = LocalDateTimeDeserializer. class ) @JsonSerialize( using = LocalDateTimeSerializer. class ) // 对入参进行格式化 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8" ) // 对出参进行格式化 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss" ) private LocalDateTime createTime; /** * 更新时间 */ @TableField(value = "update_time" , fill = FieldFill.UPDATE) @JsonDeserialize( using = LocalDateTimeDeserializer. class ) @JsonSerialize( using = LocalDateTimeSerializer. class ) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8" ) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss" ) private LocalDateTime updateTime; /** * 删除状态,0表示未删除,1表示已删除 */ @TableField(value = "deleted" ) private Integer deleted; /** * 创建时间,使用MyBatis Plus自动填充功能 */ @TableField(value = "create_at" , fill = FieldFill.INSERT) private String createAt; /** * 更新时间,使用MyBatis Plus自动填充功能 */ @TableField(value = "update_at" , fill = FieldFill.UPDATE) private String updateAt; @TableField(exist = false ) private static final long serialVersionUID = 1L; }
CacheNameTimeConstant:定义缓存的过期时间
/** * @ClassName CacheNameTimeConstant * @Author zhangzhixi * @Description 定义键的过期时间 * @Date 2023-01-05 22:30 * @Version 1.0 */ public interface CacheNameTimeConstant { String CACHE_DEFAULT = "CACHE_DEFAULT" ; String CACHE_5SECS = "CACHE_5SECS" ; String CACHE_10SECS = "CACHE_10SECS" ; String CACHE_30SECS = "CACHE_30SECS" ; }
CaffeineConfig:缓存配置
import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author zhixi */ @Configuration @EnableCaching public class CaffeineConfig { @Bean public CacheManager caffeineCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); List<CaffeineCache> caches = new ArrayList<>(); caches.add(new CaffeineCache(CacheNameTimeConstant.CACHE_5SECS, Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build())); caches.add(new CaffeineCache(CacheNameTimeConstant.CACHE_10SECS, Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build())); caches.add(new CaffeineCache(CacheNameTimeConstant.CACHE_30SECS, Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).build())); cacheManager.setCaches(caches); return cacheManager; } }
Service层
UserService
/** * @author zhangzhixi * @description 针对表【user】的数据库操作Service * @createDate 2022-05-01 12:15:07 */ public interface UserService extends IService { /** * 根据id查询用户 * * @param userId 用户id * @return 用户信息 */ User selectByIdUser(Long userId); /** * 更新用户 * @param user 实体 * @return =1更新成功,否则更新失败 */ User updateUser(User user); /** * 删除用户 * @param userId 用户id * @return =1删除成功,否则删除失败 */ boolean delUser(Long userId); }
UserServiceImpl
注解的参数包括:
- cacheNames:指定缓存的名称。
- key:指定缓存的 key。这里的 key 是一个 SpEL 表达式,表示使用方法的参数 userId 作为 key。
- unless:指定当方法的返回值为 null 时,不将结果缓存。
unless这个注解的作用是,当调用这个方法时,会先在本地缓存中查找是否已经缓存了结果,如果有,则直接返回缓存中的结果;
如果没有,则执行方法体,并将方法的返回值缓存到本地缓存中,供下次使用。如果方法的返回值为 null,则不会将结果缓存。
package com.zhixi.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.zhixi.config.CacheNameTimeConstant; import com.zhixi.mapper.UserMapper; import com.zhixi.pojo.User; import com.zhixi.service.UserService; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author zhangzhixi * @description 针对表【user】的数据库操作Service实现 * @createDate 2022-05-01 12:15:07 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Resource private UserMapper userMapper; /** * 添加缓存 */ @Override @Cacheable(key = "#userId", value = CacheNameTimeConstant.CACHE_10SECS, unless = "#result==null") public User selectByIdUser(Long userId) { return userMapper.selectById(userId); } /** * 更新缓存 */ @Override @CachePut(key = "#user.id", value = CacheNameTimeConstant.CACHE_10SECS, unless = "#result==null") public User updateUser(User user) { return userMapper.updateById(user) == 1 ? user : null; } /** * 删除缓存 */ @Override @CacheEvict(key = "#userId", value = CacheNameTimeConstant.CACHE_10SECS) public boolean delUser(Long userId) { return userMapper.deleteById(userId) == 1; } }
Controller层
import com.zhixi.pojo.User; import com.zhixi.service.UserService; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; /** * @ClassName UserController * @Author zhangzhixi * @Description * @Date 2022-5-1 12:15 * @Version 1.0 */ @RestController @RequestMapping( "/user" ) public class UserController { /** * 日志 */ private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserController. class ); @Resource private UserService userService; @RequestMapping( "/getUserById/{id}" ) public User getUserById(@PathVariable( "id" ) Long id) { User user = new User(); User queryUser = userService.selectByIdUser(id); if (queryUser != null ) { user = queryUser; } return user; } @PostMapping( "/updateUser" ) public String updatetUser(@RequestBody User user) { return userService.updateUser(user) != null ? "更新成功" : "更新失败" ; } @PostMapping( "/deleteUser/{id}" ) public String deleteUser(@PathVariable( "id" ) Long id) { return userService.delUser(id) ? "删除成功" : "删除失败" ; } }
然后使用postman进行测试即可:
简单来说就是查询和更新操作会将DB查询的数据放到内存缓存中,下次如果是同样的请求过来,那么就会将此数据直接从内存中返回。
如果删除了缓存,那么下次再执行get操作,就需要重新从DB中去查询
三、使用缓存:CaffeineCacheManager方式
可以发现,代码的变化并不大,无非是配置文件中创建CacheManager的方式不一样了,还有使用注解上面有一些小差异,要手动指定使用的缓存名称。
CaffeineConfig:缓存配置
package com.zhixi.config; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author zhixi */ @Configuration @EnableCaching public class CaffeineConfig { @Bean public CacheManager localEntityCacheManager() { CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager(); Caffeine<Object, Object> caffeine = Caffeine.newBuilder() // 初始大小 .initialCapacity(10) // 最大容量 .maximumSize(100) // 打开统计 .recordStats() // 5分钟不访问自动丢弃 .expireAfterAccess(5, TimeUnit.MINUTES); caffeineCacheManager.setCaffeine(caffeine); // 设定缓存器名称 caffeineCacheManager.setCacheNames(getNames()); // 值不可为空 caffeineCacheManager.setAllowNullValues(false); return caffeineCacheManager; } private static List<String> getNames() { List<String> names = new ArrayList<>(1); names.add("localEntityCache"); return names; } }
Service层
UserServiceImpl
package com.zhixi.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.zhixi.mapper.UserMapper; import com.zhixi.pojo.User; import com.zhixi.service.UserService; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author zhangzhixi * @description 针对表【user】的数据库操作Service实现 * @createDate 2022-05-01 12:15:07 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Resource private UserMapper userMapper; /** * 添加缓存 */ @Override @Cacheable(cacheNames = "localEntityCache", key = "#userId",unless = "#result==null") public User selectByIdUser(Long userId) { return userMapper.selectById(userId); } /** * 更新缓存 */ @Override @CachePut(cacheNames = "localEntityCache", key = "#user.id",unless = "#result==null") public User updateUser(User user) { return userMapper.updateById(user) == 1 ? user : null; } /** * 删除缓存 */ @Override @CacheEvict(cacheNames = "localEntityCache", key = "#userId") public boolean delUser(Long userId) { return userMapper.deleteById(userId) == 1; } }