知识点
Spring高版本引入了Cache的注解技术。该技术是一种规范,官方支持的实现包括Generic、JCache(JSR-107)、(EhCache 3,Hazelcast,Infinispan,and others)、EhCache 2.x、Hazelcast、Infinispan、Couchbase、Redis、Caffeine、Simple等。
Redis的Cache技术,底层使用的是Spring Data Redis
Cache技术主要有以下注解@EnableCaching
、@Cacheable
、@CacheEvict
、@CachePut
、@Caching
、@CacheConfig
,这些注解支持Spring SPEL表达式
注解详解
@EnableCaching
:作用于配置类,用来打开SpringBoot的Cache自动装配
@Cacheable
:表示被调用的方法先要去缓存中查询,如果命中则返回缓存中的内容,否则调用方法返回并将返回结果存入缓存。
@CacheEvict
:表示方法结束后删除缓存,可以通过beforeInvocation=true
设置方法执行前删除缓存
@CachePut
:用来更新缓存,旧的值会被覆盖,方法一定会执行
@Caching
:多个缓存符合操作
@CacheConfig
:标注在类上,对类中的方法统一设置缓存操作
Cache配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
/**
* 基于Redis的缓存配置
*
* @author √Angelの爱灬
* @date 2023/5/29
*/
@Configuration
@EnableCaching
public class SpringCacheRedisConfig {
@Value("${spring.application.name}")
private String appName;
/**
* Cache key generator. Used for creating a key based on the given method (used as context) and its parameters.<br/>
* className:methodName:param1-param2...
*
* @return KeyGenerator
*/
@Bean
public KeyGenerator wiselyKeyGenerator() {
return (object, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(object.getClass().getName()).append(":");
sb.append(method.getName()).append(":");
for (Object obj : params) {
sb.append(obj.toString()).append("-");
}
return sb.substring(0, sb.length() - 1);
};
}
/**
* CacheManager backed by a Redis cache.
*
* @param factory Thread-safe factory of Redis connections.
* @return RedisCacheManager
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(factory)
// 默认缓存配置
.cacheDefaults(defaultCacheConfig(Duration.ofDays(1)))
// 针对于指定key做自定义操作
.withInitialCacheConfigurations(initCacheConfig())
.transactionAware()
.build();
}
/**
* 设置redis中value的序列化方式
*
* @return Jackson2JsonRedisSerializer
*/
private Jackson2JsonRedisSerializer<Object> redisValueSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
// 指定序列化的作用域(field、get()、set()等)和作用域的访问修饰符
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类不可以是被final修饰的
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// 设置时间格式序列化及反序列化
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DatePattern.NORM_DATE_FORMATTER));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DatePattern.NORM_TIME_FORMATTER));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DatePattern.NORM_DATE_FORMATTER));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DatePattern.NORM_TIME_FORMATTER));
objectMapper.registerModule(javaTimeModule);
serializer.setObjectMapper(objectMapper);
return serializer;
}
/**
* 设置默认缓存配置
*
* @param duration key的有效时间(Duration.ZERO为永不过期)
* @return RedisCacheConfiguration
*/
private RedisCacheConfiguration defaultCacheConfig(Duration duration) {
return RedisCacheConfiguration.defaultCacheConfig()
// 格式化cacheName,默认为 cacheName::
.computePrefixWith(cacheName -> String.format("%s:%s:", appName, cacheName))
// key的默认超时时间
.entryTtl(duration)
// key的序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
// value的序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisValueSerializer()))
// 不缓存空值
.disableCachingNullValues();
}
/**
* SpringBoot Cache 中特殊key的配置,这里只针对某些key设置超时时间
*
* @return key——对应的配置
*/
private Map<String, RedisCacheConfiguration> initCacheConfig() {
Map<String, RedisCacheConfiguration> configMap = new HashMap<>(16);
configMap.put("user", defaultCacheConfig(Duration.ofHours(8)));
configMap.put("cacheable", defaultCacheConfig(Duration.ZERO));
return configMap;
}
}