一、了解缓存配置

  先来了解一下配置方法吧,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;
}
}