一. POM解析
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.5.RELEASE</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>jcl-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
可以看到主要引入了三个依赖:
1.spring-boot-starter:该包引入了starter完成自动装配所必须的class和注解等,包含了自动装配的核心逻辑,springboot中默认可以自动装配的starter也定义在其中的spring-boot-autoconfigure中的spring.factories文件中,本文要讲的RedisAutoConfiguration就定义在其中。有了这个包后,即拥有了自动装配的能力了,例如新建一个springboot项目,pom中只引入spring-boot-starter-data-redis包,其就能自动装配了。
2.spring-data-redis:spring对redis相关操作的人性化封装,使得redis的操作只需简单的调用接口即可,redis的操作的实现过程则有lettuce或jedis驱动(客户端)实现,spring-data-redis只是在接口层对他们做了统一。spring-data-redis包依赖了jedis和lettuce-core这两个驱动,但是optional为true,即这两个依赖不会传递到spring-boot-starter-data-redis中,但是该starter肯定是要依赖一个驱动的,那么其引入了第三个包,即将lettuce做为其默然实现。
3.lettuce-core:spring-boot-starter-data-redis选择的默然驱动
二. Lettuce和Jedis的区别
Lettuce 是一个可伸缩的线程安全的 Redis 客户端,支持同步、异步和响应式模式。多个线程可以共享一个连接实例,而不必担心多线程并发问题。它基于优秀 Netty NIO 框架构建,支持 Redis 的高级功能,如 Sentinel,集群,流水线,自动重新连接和 Redis 数据模型
Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接 Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
从 Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端。当然 Spring Boot 2.x 仍然支持 Jedis,并且你可以任意切换客户端,只需排除io.lettuce:lettuce-core并添加redis.clients:jedis即可。
三. 自动装配源码解析
自动装配类RedisAutoConfiguration在spring-boot-autoconfigure中已经提供,故spring-boot-starter-data-redis.jar中没有再去提供该class。
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
首先Redis的自动化配置依靠的是RedisAutoConfiguration,其会按照顺序分别引入LettuceConnectionConfiguration和JedisConnectionConfiguration,它们都会判断Spring容器中是否存在ConnectionFactory,不存在则创建。正是这个引入的顺序,导致LettuceConnectionConfiguration要比JedisConnectionConfiguration先执行,所以当LettuceConnectionConfiguration创建了ConnectionFactor后,JedisConnectionConfiguration判断不为空而不继续创建了。所以即使我们引入了Jedis依赖,最后也还是使用Lettuce客户端。
四. 修改RedisTemplate的序列化器
RedisTemplate间接实现了InitializingBean,故在Bean的生命周期内会被回调其afterPropertiesSet方法,此方法用来初始化key和value的序列化器,默认是JDK序列化器,但该序列化器需要数据对象实现Serializable接口,最后显示声明序列化ID,容易出错。可以修改利用json的序列化与反序列化能力,系列化器修改如下:
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置值(value)的序列化采用FastJsonRedisSerializer。
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}