常规CRUD快速接入Spring Cache
Spring Cache注解
注解 | 解释 |
---|---|
Cacheable | 查询缓存 |
CachePut | 更新缓存 |
CacheEvict | 删除缓存 |
Caching | 聚合缓存策略 |
CacheConfig | 缓存配置 |
CRUD改造
实体的增删改查进行缓存注解的标记
package com.wuhanpe.task.service;
import com.wuhanpe.task.entity.TaskBase;
import com.wuhanpe.task.repository.TaskRepository;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
@Service
@RequiredArgsConstructor
@Slf4j
@CacheConfig(cacheNames = {"task"})
public class TaskCacheService {
@NonNull
private TaskRepository taskRepository;
@CachePut(key = "#taskBase.id")
public int insertTask(TaskBase taskBase) {
return taskRepository.insert(taskBase);
}
@CachePut(key = "#taskBase.id")
public int updateTask(TaskBase taskBase) {
return taskRepository.updateById(taskBase);
}
@CacheEvict(key = "#id")
public int deleteTask(long id) {
return taskRepository.deleteById(id);
}
@Cacheable(key = "#taskBase.id")
public TaskBase selectTask(TaskBase taskBase) {
return taskRepository.selectById(taskBase.getId());
}
}
发起请求
第一笔
走数据库的请求
耗时是625ms
日志
第二笔
第二笔及后续的几笔走了缓存
响应时间大幅降低,同时没有新的日志产生
缓存容器
上述采用的是系统内存做为默认缓存。我们可以将缓存改为redis,可以方便其他服务器进行内存共享。
Redis缓存
添加依赖
implementation('org.springframework.boot:spring-boot-starter-data-redis')
implementation('org.apache.commons:commons-pool2:2.11.1')
配置cacheManager
@Bean("redisCacheManager")
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.create(redisConnectionFactory);
}
配置文件
spring
redis:
database: 4
host: redis.whpe
port: 6379
password: Whpe!@#123
lettuce:
pool:
#连接池最大连接数(使用负值没有限制)默认8
max-active: 64
#连接池最大阻塞等待时间(使用负值没有限制)默认-1
max-wait: -1
#连接池最大空闲连接(使用负值没有限制)默认8
max-idle: 8
#连接池最小空闲连接(使用负值没有限制)默认0
min-idle: 0
缓存类上的使用
@CacheConfig(cacheNames = {"task"}, cacheManager = "redisCacheManager")
引用上述定义的bean
redis显示
可以看到缓存已经写入,并生效。实际体验下来会比内存的慢个1~2ms左右,毕竟发生了一次网络io查询。然而发现写入到redis中的是对象格式,无法直观的看数据内容,不方便调试,因此需要进行redis序列化器的配置。
😀配置 :
@Bean("redisCacheManager")
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
RedisCacheManager redisCacheManager = new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), redisCacheConfiguration);
return redisCacheManager;
}
配置了value序列化采用jackson进行序列化。redis中显示:
CaffineCache缓存
添加依赖
implementation('com.github.ben-manes.caffeine:caffeine:2.8.8')
配置cacheManager
@Primary
@Bean("caffeineCacheManager")
public CacheManager cacheManager() {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCacheSpecification("");
return caffeineCacheManager;
}
配置文件
咖啡因缓存框架是基于内存的处理,提供了读过期和写过期的配置,当一定时间过后,缓存失效,读取数据库中的数据写入到缓存中,并进行返回。
缓存类上的使用
@CacheConfig(cacheNames = {"task"}, cacheManager = "caffeineCacheManager")
只需要修改cacheManager为上面配置的cacheManager即可
联合使用
调用更新缓存,然后调用查询缓存,看是否发生了变化 😅 注意点:更新缓存的返回值需要重新从数据库中获取最新的值。
@CachePut(key = "#taskBase.id")
public TaskBase updateTask(TaskBase taskBase) {
taskRepository.updateById(taskBase);
return taskRepository.selectById(taskBase.getId());
}
- 插入,查询联合使用
@CachePut(key = "#taskBase.id")
public TaskBase insertTask(TaskBase taskBase) {
taskRepository.insert(taskBase);
return taskBase;
}
也是需要返回TaskBase对象。实际上都是将返回值设置到缓存中,有一个注意点,缓存返回的时候需要保证自增id是设置了,防止缓存中的id为空。
@TableId(type = IdType.AUTO)
private Long id;
- 删除,查询联合使用
@CacheEvict(key = "#id")
public int deleteTask(long id) {
return taskRepository.deleteById(id);
}
删除后,查询直接返回null。达到要求
idea支持
idea已经对cache缓存单独提供了图标支持,点击CacheConfig和其他的Cache注解时可以提示缓存影响的方法
缓存过期
咖啡因缓存框架是可以设置过期时间,到了过期时间,重新从数据库获取缓存加载到内存中。
- 写入过期
连续发起请求,10s钟后,缓存失效,从数据库中重新获取,写入到缓存
- 读过期
连续写入数据,只会在最后一次访问数据过后5S钟,数据过期。访问数据的间隔一直没有大于5S,数据会一直在缓存中。
- 组合过期规则
组合规则会同时生效,各自独立的刷新缓存
可以看到时间是两个缓存过期策略都起了作用。
- refreshAfterWrite
设置写入后什么时候进行刷新,需要设置LoadingCache
Caused by: java.lang.IllegalStateException: refreshAfterWrite requires a LoadingCache
手动实现缓存加载器
private class CustomCacheLoader implements CacheLoader {
@Nullable
@Override
public Object load(@NotNull Object key) throws Exception {
return null;
}
@Nullable
@Override
public Object reload(@NotNull Object key, @NotNull Object oldValue) throws Exception {
TaskBase taskBase = (TaskBase) oldValue;
taskBase.setTaskName("121212121212");
return taskBase;
}
}
覆盖reload方法,进行返回对象的修改,同理我们也可以改到从其他的方式上去获取对象。
😀配置:
有一个点要注意,设置缓存加载器要早于配置缓存名称,否则会启动失败。
缓存参数
initialCapacity | int | 初始容量 |
---|---|---|
maximumSize | Long | 最大容量 |
maximumWeight | Long | 缓存的最大权重 |
expireAfterAccess | Duration | 写和读过期 |
expireAfterWrite | Duration | 写过期 |
refreshAfterWrite | Duration | 写后自动刷新,需要配合CacheLoader使用 |
支持哪些缓存框架
redis |
---|
caffeine |
guava |
ehcache |
jcache |
ConcurrentMapCache(默认) |
多级缓存
使用CompositeCacheManager组合两个缓存manager即可
转载自:https://juejin.cn/post/7205125350392627260