背景
SprinbBoot的cache是不是支持动态设置缓存注解的,因此本次自己实现一个可以动态设置缓存时间的配置。
步骤
引入依赖
<!-- springboot整合redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.6</version>
</dependency>
<!-- springboot cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.4</version>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
yml 配置
spring:
cache:
type: redis
redis:
database: 0
host: localhost
port: 6379
password:
key:
prefix: CACHE_
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
timeout: 10000
自定义一个RedisCacheManager
import lombok.SneakyThrows;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import java.time.Duration;
/**
* @author outengfei
* @date 2023/2/7 9:03
*/
public class CustomRedisCacheManager extends RedisCacheManager {
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
@SneakyThrows
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfiguration){
if(!name.isEmpty() && name.contains(":")){
String[] spelStr = name.split(":");
String key = spelStr[0];
String valueStr = spelStr[1];
int cycleTime = Integer.parseInt(valueStr);
return super.createRedisCache(key, cacheConfiguration.entryTtl(Duration.ofSeconds(cycleTime)));
}
return super.createRedisCache(name, cacheConfiguration);
}
}
使用自定义的RedisCacheManager覆盖springBoot的RedisCacheManager
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Value;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author outengfei
* @date 2023/2/7 9:04
*/
@Configuration
public class RedisConfig {
@Value("${spring.redis.key.prefix}")
private String keyPreFix;
/**
* redis序列化方式
* @param factory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
//指定Jackson2JsonRedisSerializer为Object的序列化器,替代redis默认的序列化器JdkSerializationRedisSerializer
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
ObjectMapper objectMapper = new ObjectMapper();
//注意:enableDefaultTyping方法已过期
// objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
//扩展知识:JsonTypeInfo.As属性认知
//JsonTypeInfo.As.PROPERTY:作为数据的兄弟属性
//JsonTypeInfo.As.EXISTING_PROPERTY:作为POJO中已经存在的属性
//JsonTypeInfo.As.EXTERNAL_PROPERTY:作为扩展属性
//JsonTypeInfo.As.WRAPPER_OBJECT:作为一个包装的对象
//JsonTypeInfo.As.WRAPPER_ARRAY:作为一个包装的数组
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
//反序列化时智能识别变量名(识别没有按驼峰格式命名的变量名)
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//反序列化如果有多的属性,不抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化如果碰到不识别的枚举值,是否作为空值解释,true:不会抛不识别的异常, 会赋空值,false:会抛不识别的异常
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/*
* @description Redis缓存的序列化方式使用redisTemplate.getValueSerializer(),不在使用JDK默认的序列化方式
* @param redisTemplate
* @return RedisCacheManager
**/
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
//使用prefixCacheNameWith需要注意系统自动拼接的双”:“问题
.computePrefixWith(cacheName -> keyPreFix + ":" + cacheName + ":")
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer()));
//return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
return new CustomRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
}
测试
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* @author outengfei
* @date 2023/2/7 9:14
*/
@Slf4j
@Service
public class TestService {
/**
* value 【:后面代表缓存时间 单位 /秒】
* @param key
* @return
*/
@Cacheable(value = "data:200000", key = "#key")
public Object getTestData(String key){
return System.currentTimeMillis();
}
/**
* value 【:后面代表缓存时间 单位 /秒】
* @param key
* @return
*/
@Cacheable(value = "date:5", key = "#key")
public Object getTestDate(String key){
return LocalDateTime.now().toString();
}
/**
* 删除缓存
* @param key
*/
@CacheEvict(value = "data", key = "#key")
public void delete(String key) {
}
}
评论区