一、什么是Spring Cache
从spring 3.1版本开始,提供了一种透明的方式来为现有的spring 应用添加cache。在应用层面与后端存储之间,提供了一层抽象,这层抽象目的在于封装各种可插拔的后端存储( ehcache guava redis),最小化因为缓存给现有业务代码带来的侵入。
核心抽象主要体现在两个接口上
org.springframework.cache.cache
org.springframework.cache.cachemanager
cache代表缓存本身,包含缓存的常用操作:增加、删除、读取等。
CacheManager 是 Spring 各种缓存的抽象接口。抽象的意义在于屏蔽实现细节的差异和提供扩展性,这一层cache的抽象解耦了缓存的使用和缓存的后端存储,这样后续可以方便的更换后端存储。
Spring 支持的常用 CacheManager 如下:
CacheManager | 描述 |
SimpleCacheManager | 使用简单的 Collection 来存储缓存 |
ConcurrentMapCacheManager | 使用 java.util.ConcurrentHashMap 来实现缓存 |
NoOpCacheManager | 仅测试用,不会实际存储缓存 |
EhCacheCacheManger | 使用EhCache作为缓存技术。EhCache 是一个纯 Java 的进程内缓存框架,特点快速、精干,是 Hibernate 中默认的 CacheProvider,也是 Java 领域应用最为广泛的缓存 |
JCacheCacheManager | 支持JCache(JSR-107)标准的实现作为缓存技术 |
CaffeineCacheManager | 使用 Caffeine 作为缓存技术。用于取代 Guava 缓存技术。 |
RedisCacheManager | 使用Redis作为缓存技术 |
HazelcastCacheManager | 使用Hazelcast作为缓存技术 |
CompositeCacheManager | 用于组合 CacheManager,可以从多个 CacheManager 中轮询得到相应的缓存 |
二、核心注解
Spring Cache 提供了 @Cacheable 、@CachePut 、@CacheEvict 、@Caching 等注解
1、 @Cacheable
缓存数据或者获取缓存数据
属性值如下:
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
keyGenerator | key的生成器。key/keyGenerator二选一使用 |
cacheManager | 指定缓存管理器 |
cacheResolver | 指定获取解析器 |
condition | 条件符合则缓存 |
unless | 条件符合则不缓存 |
sync | 是否使用异步模式,默认为false |
2、@CachePut
修改缓存数据。
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
keyGenerator | key的生成器。key/keyGenerator二选一使用 |
cacheManager | 指定缓存管理器 |
cacheResolver | 指定获取解析器 |
condition | 条件符合则缓存 |
unless | 条件符合则不缓存 |
3、@EnableCaching
开启缓存功能,一般放在启动类上
4、@CacheEvict
清空缓存
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
keyGenerator | key的生成器。key/keyGenerator二选一使用 |
cacheManager | 指定缓存管理器 |
cacheResolver | 指定获取解析器 |
condition | 条件符合则缓存 |
allEntries | 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存 |
beforeInvocation | 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存 |
5、@Caching
该注解可以实现同一个方法上同时使用多种注解。
6、@CacheConfig
统一配置@Cacheable中的value值
三、接入Redis简单案例demo
1、添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${version}</version>
</dependency>
2、redis配置
spring:
redis:
# Redis数据库索引(默认为0)
database: 0
host: 127.0.0.1
password: test
# Redis服务器连接端口
port: 6379
# 连接超时时间(毫秒)
timeout: 10000
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 8
# 连接池中的最小空闲连接
min-idle: 0
3、配置CacheManager以及自定义key生成策略
@EnableConfigurationProperties(CacheProperties.class)
@Configuration(proxyBeanMethods = false)
public class RedisCacheAutoConfiguration extends CachingConfigurerSupport {
@Resource
private RedisConnectionFactory factory;
@Resource
private CacheProperties cacheProperties;
/**
* 自定义生成redis-key
*/
@Override
@Primary
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder(cacheProperties.getCacheKeyPrefix()).append(":");
sb.append(target.getClass().getName()).append(":");
sb.append(method.getName()).append(":");
for (Object param : params) {
sb.append(param.toString()).append(":");
}
return sb.toString();
};
}
@Override
@Bean
@Primary
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
/**
* 配置CacheManager
*/
@Primary
@Bean
@Override
public CacheManager cacheManager() {
RedisCacheConfiguration defaultRedisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
//不缓存null结果
// .disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(CommonUtil.objectMapper())))
//默认缓存30分钟
.entryTtl(Duration.ofMinutes(30))
//设置缓存key前缀
.computePrefixWith(cacheKeyPrefix());
return new CustomRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory), defaultRedisCacheConfiguration);
}
/**
* key添加默认前缀方法
*/
@Primary
@Bean
public CacheKeyPrefix cacheKeyPrefix() {
return (String cacheName) -> {
String[] arr = cacheName.split(CustomRedisCacheManager.SEPARATOR);
StringBuilder sb = new StringBuilder(cacheProperties.getCacheKeyPrefix()).append(":").append(arr[0]).append(":");
return sb.toString();
};
}
}
默认key前缀配置
@Data
@ConfigurationProperties(prefix = "test.cache")
public class CacheProperties {
/**
* 缓存key前缀
*/
private String cacheKeyPrefix;
}
自定义RedisCacheManager用于重写过期时间
/**
* 继承RedisCacheManager,重写createRedisCache方法,定义缓存过期时间
*/
public class CustomRedisCacheManager extends RedisCacheManager {
public static final String SEPARATOR = "#";
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
String[] arr = StringUtils.delimitedListToStringArray(name, SEPARATOR);
name = arr[0];
if (arr.length > 1) {
// 缓存过期时间,注意单位是秒
long ttl = Long.parseLong(arr[1]);
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
}
return super.createRedisCache(name, cacheConfig);
}
}
使用案例
//缓存失效时间1800s,key为拼接nick
@Cacheable(value = "taoOrderAnalysis#1800", key = "#nick")
public TaoOrderAnalysisDianzhang queryByNick(String nick) {
TableRouter tableRouter = new TableRouter();
tableRouter.setDbNo(1);
tableRouter.setLogicTable("tao_order_analysis_dianzhang");
return taoOrderAnalysisDianzhangMapper.queryByNick(nick, tableRouter);
}
在传递的nick为 -fengye- ,定义的cacheKeyPrefix为 test ,那么获取缓存使用key为:test:taoOrderAnalysis:-fengye-