🧃SpringBoot整合Caffeine(注解方式)
一、了解缓存配置
先来了解一下配置方法吧,SimpleCacheManager和CaffeineCacheManager配置的区别:
1 2 3 4 5 6 7 8 9 | SimpleCacheManager: 这种缓存管理器允许你在应用程序启动时通过配置多个CaffeineCache来创建多个缓存。 这种方式可以让你为每个方法单独配置缓存过期时间。 CaffeineCacheManager: 这种缓存管理器使用了一个全局的Caffeine配置来创建所有的缓存。 这种方式不能为每个方法单独配置缓存过期时间,但是可以在程序启动时配置全局的缓存配置,这样就可以轻松地设置所有方法的缓存过期时间。 总结: 如果你希望为每个方法单独配置缓存过期时间,那么建议使用第一种方式。 否则,如果你希望设置全局的缓存配置,那么建议使用第二种方式。 |
本文使用MP进行构建,Gitee地址:https://gitee.com/zhang-zhixi/springboot-caffeine.git
二、使用缓存:SimpleCacheManager方式
实体类:User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 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:定义缓存的过期时间
1 2 3 4 5 6 7 8 9 10 11 12 13 | /** * @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:缓存配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /** * @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,则不会将结果缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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:缓存配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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 ; } } |
标签:
SpringBoot
, SpringCache
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?