spring-cache是spring框架中的一个缓存抽象层,
它提供了一种简便的方式来集成不同的底层缓存实现,
如内存缓存(concurrentMap/ehcache/caffeine)/分布式缓存(redis/couchbase)等
它简化了在app中使用缓存的逻辑,并提供了一组注解和API来实现缓存功能
1.声明性缓存
spring-cache通过注解的方式,允许开发者在方法级别上声明方法的结果要被缓存。
相关注解有@Cacheable 读取缓存+不存在则缓存
@CacheEvict 清除缓存
@CachePut 强制缓存
@Caching 复合功能注解==(@Cacheable+@CacheEvict+@CachePut)
2.缓存的透明性
spring-cache提供了一致的编程接口,
无论底层使用哪种缓存技术,开发者都可以使用相同的方式访问和管理缓存。
这可以使app轻松切换/替换不同的缓存实现技术,而无需更改业务代码
3.注解定义缓存策略
可以使用注解@Cacheable/@CacheEvict/@CachePut 声明缓存的行为和策略
如声明缓存名称,缓存key
@Cacheable(cacheNames = "user", key = "#id")
会按照key=user::id缓存数据 (可以参见spring-cache-redis缓存效果)
4.支持SpEL表达式
使用SpEL表达式,可以定义缓存的键、条件等。
这允许开发者根据方法参数、方法返回值等动态生成缓存键或决定是否应用缓存。
@Cacheable(cacheNames = "user", key = "#id",condition = "#id != null ")
public UserDO getById(Long id) {
returnuserRepo.findById(id).orElse(null);
}
这个案例只有当id不为空时,才进行缓存
condition属性可以设置缓存的条件,如
#id >=100
#userInfo.id >10
#id%2!=0
等等,必须确保condition能正确返回布尔值,才能决定当前方法最终是否进行缓存
caffeine是一种java内存缓存技术,支持多种缓存策略,
caffeine可以单独使用于普通java项目/springboot/springcloud项目,
这里引入caffeine来作为spring-cache存取缓存的数据区。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>com.github.ben-manes.caffeinegroupId>
<artifactId>caffeineartifactId>
<version>2.8.0version>
dependency>
需要在application.yml配置spring-cache使用caffeine
spring:
cache:
type: caffeine #设置spring-cache框架使用caffeine存取数据
package cn.test.cache;
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.concurrent.TimeUnit;
@EnableCaching
@Configuration
public class CacheCfg {
@Bean
public CacheManager cacheManager(){
//定义要缓存的caccheName有哪些
CaffeineCacheManager caffeineCacheManager =
new CaffeineCacheManager("user");
//设置缓存配置
caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
//初始容量10
.initialCapacity(10)
//最大容量200
.maximumSize(200)
//写入过期时间30s
.expireAfterWrite(30, TimeUnit.SECONDS));
return caffeineCacheManager;
}
}
/**
* @Cacheable注解标记一个方法时,spring会在执行方法之前,先检查缓存中是否已存在该方法的返回结果
* 如果存在,则直接返回缓存的结果,不执行方法的实际逻辑
* 如果不存在,则执行方法并将结果保存到缓存中
*/
@Cacheable(cacheNames = "user", key = "#id")
public UserDO getById(Long id) {
return userRepo.findById(id).orElse(null);
}
/**
* @CacheEvict注解标记一个方法时,spring会在方法执行成功后,
* 清空指定的缓存项,以确保下次访问时可以重新计算或查询最新的结果
*/
@CacheEvict(cacheNames = "user", key = "#id")
public void delUser(Long id) {
UserDO userDO = userRepo.findById(id).orElse(null);
if (userDO != null) {
userRepo.delete(userDO);
}
}
/**
* @CachePut注解标记一个方法时,spring会在方法执行后,
* 将返回值放入指定的缓存中,
* 以便将来的访问可以直接从缓存中获取结果,而不需要再执行方法的实际逻辑
*
* @CachePut注解,适用于 创建新缓存 或 强制更新缓存 操作
*/
@CachePut(cacheNames = "user", key = "#userInfo.id")
public UserDO addUser(UserDO userInfo) {
return userRepo.save(userInfo);
}
/**
* 执行updateUserInfo方法时,这里会按key 强制更新caffeine缓存
*/
@CachePut(cacheNames = "user", key = "#userInfo.id")
public UserDO updateUserInfo(UserDO userInfo) {
UserDO userDO = userRepo.findById(userInfo.getId()).orElse(null);
if (userDO != null) {
BeanUtils.copyProperties(userInfo,userDO);
userRepo.save(userDO);
}
return userDO;
}
redis是一种分布式缓存技术,不限语言,C/S架构,支持可视化观测,
redis可以单独使用于普通java项目/springboot/springcloud项目/其他语言项目等,
这里引入redis来作为spring-cache框架存取缓存的数据区。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.5.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
spring:
redis:
# ip
host: localhost
# 端口6379
port: 6379
#密码,没有密码则不配置这一项
password:
#指定使用redis 16个库中的哪一个,不配置的话,默认配置为0
database: 2
lettuce:
pool:
min-idle: 0 #连接池最新空闲时间
max-wait: -1ms #最大等待时间
max-active: 8 #最大活跃时间
max-idle: 8 #最大空闲时间
shutdown-timeout: 100ms #连接池关闭超时时间
timeout: 1000ms #redis连接超时时间
cache:
type: redis #设置spring-cache框架使用redis存取数据
package cn.test.cache;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@EnableCaching
@Configuration
public class RedisConfig {
/**
* 设置RedisTemplate使用的序列化器,
* 这里使用string作为key的序列化,使用jackson作为value的序列化
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jsonSerializer);
return template;
}
/**
* 设置spring-cache使用redis后,要配置cacheManager缓存管理器
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
//设置spring-cache缓存到redis的数据有效期是60s
.entryTtl(Duration.ofSeconds(60))
//key的序列化使用字符串
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
//value的序列化使用jackson
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
}
@Cacheable(cacheNames = "user", key = "#id")
public UserDO getById(Long id) {
return userRepo.findById(id).orElse(null);
}
@CacheEvict(cacheNames = "user", key = "#id")
public void delUser(Long id) {
UserDO userDO = userRepo.findById(id).orElse(null);
if (userDO != null) {
userRepo.delete(userDO);
}
}
@CachePut(cacheNames = "user", key = "#userInfo.id")
public UserDO addUser(UserDO userInfo) {
return userRepo.save(userInfo);
}
@CachePut(cacheNames = "user", key = "#userInfo.id")
public UserDO updateUserInfo(UserDO userInfo) {
UserDO userDO = userRepo.findById(userInfo.getId()).orElse(null);
if (userDO != null) {
BeanUtils.copyProperties(userInfo,userDO);
userRepo.save(userDO);
}
return userDO;
}
因为RedisCacheManager配置了使用jackson序列化,这里缓存数据值是以json格式存储到redis的。
可以观察到spring-cache存入redis的缓存有效期是60s (因为RedisCacheManager配置了60秒);
缓存的key是
user::1
缓存的value是
{
"@class": "cn.test.orm.user.UserDO",
"id": 1,
"account": "ewr3",
"pwd": "23r3r",
"remark": "23r3r",
"createTime": [
"java.sql.Timestamp",
"2023-08-02 11:14:02"
],
"updateTime": [
"java.sql.Timestamp",
"2023-08-02 11:14:02"
],
"isDel": 0
}