1、配置
1、依赖
<!--springcache依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--使用redis作为缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、yml配置
spring:
cache:
type: redis
redis:
host: localhost
port: 6379
database: 2
jedis:
pool:
max-active: -1
max-wait: 3000ms
timeout: 3000ms
3、cache配置
package com.example.demo.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperation;
import org.springframework.cache.interceptor.CacheOperationSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.util.*;
import java.time.Duration;
import java.util.*;
@EnableCaching
@Configuration
public class SpringCacheConfig implements ApplicationContextAware {
private Set<String> cacheNames = new HashSet<>();
private static final String SEPARATOR = "#";
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration defaultConfiguration = getRedisCacheConfigurationWithTtl(2 * 24 * 3600);
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.builder(connectionFactory)
.cacheDefaults(defaultConfiguration);
if (!CollectionUtils.isEmpty(cacheNames)) {
Map<String, RedisCacheConfiguration> cacheConfigurations = new LinkedHashMap<>();
for (String cacheName : cacheNames) {
if (cacheName.contains(SEPARATOR)) {
String[] strs = StringUtils.split(cacheName, SEPARATOR);
Assert.notNull(strs);
long ttl = Long.parseLong(strs[1].trim());
RedisCacheConfiguration customizedConfiguration = getRedisCacheConfigurationWithTtl(ttl);
cacheConfigurations.put(cacheName, customizedConfiguration);
}
}
if (cacheConfigurations.size() > 0) {
builder.withInitialCacheConfigurations(cacheConfigurations);
}
}
return builder.build();
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(long ttl) {
return RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofSeconds(ttl));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
return;
}
ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
CacheOperationSource cacheOperationSource = context.getBean(CacheOperationSource.class);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(beanName);
try {
Class<?> beanClass = beanDefinition.resolveBeanClass(ClassUtils.getDefaultClassLoader());
if (beanClass != null) {
ReflectionUtils.doWithMethods(beanClass, m -> {
Collection<CacheOperation> cacheOperations = cacheOperationSource.getCacheOperations(m, beanClass);
if (!CollectionUtils.isEmpty(cacheOperations)) {
for (CacheOperation operation : cacheOperations) {
cacheNames.addAll(operation.getCacheNames());
}
}
}
);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
2、注解
@EnableCaching (开启缓存功能)
开启缓存,缓存才能生效 直接启动类上开启
@Cacheable (将数据保存到缓存)
根据方法返回结果进行缓存 ,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
拥有属性:
属性名 | 解释 |
---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间(就是文件夹名),可多个 | cacheNames | 与 value 差不多,二选一即可 | key | 可选属性,可以使用 SpringEL 标签自定义缓存的key | keyGenerator | key的生成器。key/keyGenerator二选一使用 | cacheManager | 指定缓存管理器 | cacheResolver | 指定获取解析器 | condition | 条件符合则缓存 | unless | 条件符合则不缓存 | sync | 是否使用异步模式,默认为false |
1、key参数设置,SpringEl表达式
测试 1、默认方法
默认方法解析:
- 如果缓存中有,方法不用调用
- key默认自动生成,缓存名字::SimpleKey []
value就是方法的返回值
2、指定简单key key = "'cacheableKey'"
指定key,默认是spEL表达式,所以字符串需要用单引号
@Cacheable(value = {"cacheable1","cacheable2"},key = "'cacheableKey'")
3、使用参数作为key
key = "#name" 此时的key就是code的值
或者拼接字符串
@Cacheable(value = {"cacheable1"},key = "#name + ':' + #code")
4、使用实体类里的参数 key = "#user.username"
5、更多key方法
属性名称 | 描述 | 示例 |
---|
methodName | 当前方法名 | #root.methodName | method | 当前方法 | #root.method.name | target | 当前被调用的对象 | #root.target | targetClass | 当前被调用的对象的class | #root.targetClass | args | 当前方法的参数,如取第一个参数就是args[0] | #root.args[0] | caches | 当前被调用的方法使用的cache | #root.caches[0].name |
2、其他参数示例
cacheNames 和value作用一样,分区(文件夹)
@Cacheable(cacheNames = {"cacheable1"},key = "#user")
unless 条件符合不缓存 , 如下:结果是null,不用缓存
@Cacheable(value = {"cacheable1"},key = "#code",unless = "#result == null" )
condition 条件符合缓存 , 如下:参数user的长度大于2,可以缓存
@Cacheable(value = {"cacheable1"},key = "#user",condition = "#user.length() > 2")
keyGenerator 生成key
@Cacheable(cacheNames = {"cacheable1"},keyGenerator ="keyGenerator" )
这个需要自己写生成的算法,keyGenerator =”方法名“
@Component
public class keyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
return "123";
}
}
@CacheEvict (将数据从缓存删除)
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
拥有属性:
属性名 | 解释 |
---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间(就是文件夹名),可多个 | cacheNames | 与 value 差不多,二选一即可 | key | 可选属性,可以使用 SpringEL 标签自定义缓存的key | keyGenerator | key的生成器。key/keyGenerator二选一使用 | cacheManager | 指定缓存管理器 | cacheResolver | 指定获取解析器 | condition | 条件符合则缓存 | allEntries | 是否清空所有缓存,默认为false。如果指定为true,则方法调用后将立即清空所有缓存 | beforeInvocation | 是否在方法执行前就清空,默认为false。如果指定为true,则在方法执行前就会清空缓存 |
@CacheEvict(value = {"cacheable1"},key ="'123'",allEntries = true,beforeInvocation = true)
@CachePut (不影响方法执行更新缓存)
使用该注解标志的方法,每次都会执行 ,并将结果存入指定的缓存中,会更新缓存的值 。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
拥有属性:
属性名 | 解释 |
---|
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间(就是文件夹名),可多个 | cacheNames | 与 value 差不多,二选一即可 | key | 可选属性,可以使用 SpringEL 标签自定义缓存的key | keyGenerator | key的生成器。key/keyGenerator二选一使用 | cacheManager | 指定缓存管理器 | cacheResolver | 指定获取解析器 | condition | 条件符合则缓存 | unless | 条件符合则不缓存 |
@CachePut(value = {"cacheable1"},key ="'123'")
@Caching (组合多个缓存操作)
该注解可以实现同一个方法上同时使用多种注解。各属性值可以多个,如删除多个key
拥有属性:
属性名 | 解释 |
---|
evict | 对应删除操作的注解 | cacheable | 对应存储缓存操作的注解 | put | 对应更新缓存操作的注解 |
@Caching(evict = {@CacheEvict(value = "caching",key = "'123'"),@CacheEvict(value = "caching2",key = "'321'")},
cacheable = @Cacheable(value = "caching",key = "'123'"),
put = @CachePut(value = "caching2",key = "'321'"))
@CacheConfig (在类级别共享缓存的相同配置)
当我们需要缓存的地方越来越多,你可以使用@CacheConfig(cacheNames = {“cacheName”})注解在 class 之上来统一指定value的值 ,这时可省略value,如果你在你的方法依旧写上了value,那么依然以方法的value值为准。
拥有属性:
属性名 | 解释 |
---|
cacheNames | 与 value 差不多,二选一即可 | keyGenerator | key的生成器。key/keyGenerator二选一使用 | cacheManager | 指定缓存管理器 | cacheResolver | 指定获取解析器 |
@CacheConfig(cacheNames = {"cacheName"})
|