写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!
在整合redis做缓存前先看一下我们之前缓存的时候用的是什么呢,spring默认匹配的是SimpleCacheConfiguration缓存,它的底层用了一个key-value的Map,不能像redis一样持久化,一般情况下我们是需要持久化缓存的,那就需要替换默认的SimpleCacheConfiguration。
我们可以通过在配置文件中添加debug=true来使自动配置报告打印在控制台,我们看下springboot的自动配置为我们做了什么,我们先找到自动配置类CacheAutoConfiguration源码
可以看到它为我们导入了一个CacheConfigurationImportSelector,接着看CacheConfigurationImportSelector,并打上断点启动看下它做了什么,
可以看出它为我们导入了10个缓存配置类,并且从控制台可以看到SimpleCacheConfiguration也确实匹配到了,
那他是怎么匹配上SimpleCacheConfiguration的呢,看下它的源码
可以看到类上有@Conditional派生的@ConditionalOnMissingBean注解,@Conditional的作用是必须它指定的条件成立,才给容器中添加组件,派生的一些组件是在它基础之上进行的扩展,如这个@ConditionalOnMissingBean作用是:容器中不存在指定的CacheManager时它就会被注入(即匹配到),我们并没有添加任何的CacheManager,当然会匹配到,关于@Conditional相关的注解,它就是为了方便程序根据当前环境或者容器情况来动态注入bean,可以查看spring的官方文档了解下,或者看一下网上的博客,有很多,贴一张图,
继续看SimpleCacheConfiguration,它注入了一个ConcurrentMapCacheManager,
而ConcurrentMapCacheManager实现了CacheManager接口,那他必定有一个获取缓存的方法,看下ConcurrentMapCacheManager中获取缓存的方法
可以看到,缓存对象Cache是从cacheMap中获取的,如果没有就创建一个放进去,而cacheMap是个ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
查看完源码总结一下:springboot自动配置默认匹配的是SimpleCacheConfiguration,它给容器中注入了一个ConcurrentMapCacheManager,ConcurrentMapCacheManager可以获取和创建ConcurrentMapCache类型的缓存组件,它的作用将数据保存在ConcurrentMap中。
好了,不废话了,开始整合redis吧
第一步:首先引入springboot对redis的启动依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这时候启动程序,从控制台就可以看到现在匹配到了RedisCacheConfiguration这个缓存配置类,按照上面的方法感兴趣的小伙伴可以追踪下源码,
第二步:配置文件中配置连接redis :spring.redis.host=127.0.0.1
完成!(可以使用JedisPool来作为redis连接池)
可以测试了,访问:http://localhost:8080/user/1 两次,控制台打印如下:
两次查询只访问了一次数据库,达到了缓存效果,再看下redis中:
redis中也持久化了缓存数据。
但是看缓存的内容,因为默认使用的是JdkSerializationRedisSerializer这个序列化器,导致缓存的结果根本看不懂是啥吧,我们也可以使用其他序列化方式来达到不同的需求。比如我们希望缓存的数据具有可读性就可以将其序列化为json格式,json序列化可以使用Jackson2JsonRedisSerialize或FastJsonRedisSerializer。如果我们希望拥有更快的速度和占用更小的存储空间推荐使用KryoRedisSerializer进行序列化。如下使用Jackson2JsonRedisSerialize序列化器,自定义RedisTemplate和RedisCacheManager
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间30秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(1800000))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
完成!
清空redis ,访问:http://localhost:8080/user/1 两次,控制台打印如下:
查看redis中: