版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27868061/article/details/81913409
直接上码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.CacheKeyPrefix;
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.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.format.support.DefaultFormattingConversionService;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/* 启用缓存 */
@EnableCaching
@Configuration
public class SpringCacheConfig {
private static final Logger logger = LoggerFactory.getLogger(SpringCacheConfig.class);
@Autowired
private RedisConnectionFactory factory;
/**
* 最新的redis缓存管理器,需要连接工厂来构造
* 这个返回的缓存管理器,可以覆盖配置
*/
@Bean
public CacheManager cacheManager(){
/* 缓存名称集合,使用简单配置时好像不需要配置这个,显式配置需要添加 */
Set<String> cacheNames = new HashSet<>();
cacheNames.add("cache1");
cacheNames.add("cache2");
cacheNames.add("cache3");
cacheNames.add("person");
/* 不知道啥意思 */
Duration duration = Duration.ZERO;
/* 是否缓存null值 */
boolean cacheNullValues = false;
/* 是否使用前缀,不使用key前缀时,只是用key作为redis键 */
boolean usePrefix = false;
/* 缓存键前缀,这是一个方法,根据缓存name生成key的方法,simple为name::,最后的存储key为keyPrefix加上key*/
CacheKeyPrefix keyPrefix = CacheKeyPrefix.simple();
/* 键序列化对,一般键都是字符串 */
RedisSerializationContext.SerializationPair<String> keySerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
/* 值序列化对,一般值都是对象,可以使用jdk的序列化机制,与Jackson序列化 */
RedisSerializationContext.SerializationPair<Object> valueSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
/* 这是啥 */
ConversionService conversionService = new DefaultFormattingConversionService();
/* 这个类的方法很奇怪,每个方法都返回一个新的实例,所以需要重新设置实例引用 */
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration = cacheConfiguration.computePrefixWith(keyPrefix);
//cacheConfiguration = cacheConfiguration.disableKeyPrefix();
cacheConfiguration = cacheConfiguration.disableCachingNullValues();
cacheConfiguration = cacheConfiguration.entryTtl(duration);
cacheConfiguration = cacheConfiguration.serializeKeysWith(keySerializationPair);
cacheConfiguration = cacheConfiguration.serializeValuesWith(valueSerializationPair);
cacheConfiguration = cacheConfiguration.withConversionService(conversionService);
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(cacheConfiguration)
.disableCreateOnMissingCache()
.initialCacheNames(cacheNames)
.transactionAware()
.build();
return cacheManager;
}
/**
* 缓存的key生成器
* key有两种生成方式:
* 1.直接通过key属性使用spel表达式指定
* 2.通过KeyGenerator这个Bean来实现
* 当前的效果是,如果两者都不设置,则使用一种默认的策略生成SimpleKeyGenerator
* 如果只设置其一,则使用设置的方式
* 如果两者同时设置,就会出错
*/
@Bean
public KeyGenerator keyGenerator(){
/**
* 最简单的key生成器,当参数只有一个时,只是按照参数拼凑出key,当参数为多个时,会进行拼接
* SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator();
*/
/**
* 自定义key生成器,可以按照调用对象、方法、参数生成key
*/
KeyGenerator keyGenerator = new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder builder = new StringBuilder();
builder.append(target.getClass().getName()+":");
builder.append(method.getName()+":");
for(Object object:params){
builder.append(object+",");
}
return builder.toString();
}
};
return keyGenerator;
}
/**
* 配置缓存错误处理器,当获取、设置、清除三种缓存动作出错时,处理错误
*/
@Bean
public CacheErrorHandler cacheErrorHandler(){
CacheErrorHandler handler = new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
logger.error("获取缓存出现异常");
logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
logger.error("设置缓存出现异常");
logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
logger.error("清除缓存出现异常");
logger.error("缓存名称:"+cache.getName()+",缓存key:"+key+",异常:"+exception.getLocalizedMessage());
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
logger.error("清理所有缓存出现异常");
logger.error("缓存名称:"+cache.getName()+",异常:"+exception.getLocalizedMessage());
}
};
return handler;
}
}
其实缓存这里无非牵扯一下几个问题:
1.序列化问题
一般的键都是以作为字符串来序列化,而值可以选择JDK序列化和Jackson序列化等各种形式,Jackson可以序列化为json,JDK序列化为二进制数据
2.命名空间问题
一个项目一般不会只存在一类数据需要缓存,多个不同种类数据要缓存时需要使用命名空间来进行区分
这里首先有一个usePrefix指定是否使用键前缀,如果不使用前缀,则所有缓存的key都是@Cacheable注解中指定的key
如果使用key前缀,则使用CacheKeyPrefix来指定如何从cacheName生成前缀
3.缓存形式
Redis中有多种数据结构,集合链表映射有序集合等,但实际上spring使用redis缓存时,还是使用了最普通的字符串存储
大致就是这么多