spring全家桶新成员,分析阅读Spring Data Redis源码
前言
公司项目交付完成,最近抽空阅读了SpringData-Redis的源码,
简介
Spring Data Redis是较大的Spring数据家族的一部分,它提供了从Spring应用程序轻松配置和访问redis的功能。它提供了与存储交互的低级和高级抽象,使用户从基础设施的关注中解放出来。
功能特色
作为多个ReIIS驱动程序的低层抽象的连接包
在Spring Boot中,官方提供了spring-boot-autoconfigure包和starter包用来帮助我们简化配置,比如之前要建一个Spring mvc项目,需要我们配置web.xml,dispatcherservlet-servlet.xml,applicationContext.xml等等。而在Spring Boot中只需要在pom中引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后在application.properties中配置ip,密码等必要参数
spring.redis.host=106.15.108.50
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
源码解析
在application.properties中ctrl+左击redis的相关配置项,会打开spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar中的RedisProperties。
旧版本依赖、我们可以看到旧版本的maven中默认使用了jedis连接池。
新版本依赖
通过idea的maven插件我们可以看到在最新版本的SpringBoot的spring-Data-Redis的起步依赖中去除了原先的jedis连接池,引入了lettuce连接池。
原先的jedis已经被lettuce替代。在高并发场景下jedis已经无法满足需求、jedis客户端并不是线程安全的,在开中线程安全一直是开发中需要注意的问题。springData已经不再把jedis作为默认连接池,如果在项目中使用jedis连接池需要手动开启jedis配置。
旧版本包结构
新版本源包结构
Jedis 是一个很老牌的 Redis 的 Java 开发包,使用很稳定,作者维护很勤勉,社区上能搜到的文章也非常非常多。算是使用范围最广的 Redis 开发包。但是 Jedis 比较推出时间比较早,整个设计思路比较传统,例如不支持异步操作,接口设计比较繁琐老套(相比其他开发包而已),使用连接池占用很多的物理连接资源。当然,这个是可以理解的,比较一个比较早期的开发包,相对其做大的结构调整是很难的,而且用户也不一定会接受。
关于jedis跟lettuce的区别:
- Lettuce 和 Jedis 的定位都是Redis的client,所以他们当然可以直接连接redis server。
- Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
- Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
- Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
- Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作
SpringBoot对Redis的支持.
我们可以在idea通过双击Shift建进行全局文件搜索
搜索到你要找的类点击就可以查看该类
RedisConnectionFactory
RedisCommands接口多继承各种操作命令接口
public interface RedisCommands<K, V> extends BaseRedisCommands<K, V>,
RedisClusterCommands<K, V>, RedisGeoCommands<K, V>, RedisHashCommands<K, V>,
RedisHLLCommands<K, V>, RedisKeyCommands<K, V>, RedisListCommands<K, V>,
RedisScriptingCommands<K, V>, RedisServerCommands<K, V>, RedisSetCommands<K, V>,
RedisSortedSetCommands<K, V>, RedisStreamCommands<K, V>, RedisStringCommands<K,
V>, RedisTransactionalCommands<K, V> {
String auth(String var1);
String select(int var1);
String swapdb(int var1, int var2);
StatefulRedisConnection<K, V> getStatefulConnection();
}
RedisKeyCommands接口 定了常用的key操作封装为key命令接口
就是将Redis的各种命令封装成了Java的实现方法了.
RedisConnection接口定义Redis的连接抽象方法
JedisConnectionFactory接口
已经过时的方法JedisConnectionFactory(JedisShardInfo shardInfo)
JedisConnectionFacotory从Spring Data Redis 2.0开始已经不推荐直接显示设置连接的信息了,一方面为了使配置信息与建立连接工厂解耦,另一方面抽象出Standalone,Sentinel和RedisCluster三种模式的环境配置类和一个统一的jedis客户端连接配置类(用于配置连接池和SSL连接),使得我们可以更加灵活方便根据实际业务场景需要来配置连接信息。
如果定义reidsServer的配置,springData-redis默认配置
如果使用时未传入JedisClientConfiguration 对象,
默认实例化 当前类的this.clientConfiguration ;
//无参构造
public JedisConnectionFactory() {
this((JedisClientConfiguration)(new JedisConnectionFactory.MutableJedisClientConfiguration()));
}
//重载有参构造
private JedisConnectionFactory(JedisClientConfiguration clientConfig) {
this.providedShardInfo = false;
this.convertPipelineAndTxResults = true;
this.standaloneConfig = new RedisStandaloneConfiguration("localhost", 6379);
Assert.notNull(clientConfig, "JedisClientConfiguration must not be null!");
//将传入的对象给 类变量clientConfiguration 赋值
this.clientConfiguration = clientConfig;
}
AbstractRedisConnection类定义抽象的连接方法,实现RedisConnection
RedisProperties
org.springframework.boot.autoconfigure.data.redis.RedisProperties 会根据配置自动加载为一个bean
@ConfigurationProperties(prefix = “spring.redis”)
设置绑定属性的前缀。前缀 + 前缀现在回到配置文件看是不是感觉很熟悉,这不就是我们在配置文件中配置的redis连接参数吗!如果我们没有配置端口这种属性,那么这里也会提供部分默认配置。
当然,只是这些是没办法让Spring Boot在启动时扫描到该类的,所以需要下一个类org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class。
然后我们还能找到
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
RedisAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public 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;
}
}
RedisAutoConfiguration
包位置org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
- @Configuration常见的配置注解,内部含有一个以上的@Bean,让Spring能扫描到内部的@Bean,当然在Spring
Boot中,默认只会扫描到启动类所在包或其下级包的类,所以还会通过其他的设置来让这个类被扫描到,这个后面会详细说明。
@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
- @EnableConfigurationProperties(RedisProperties.class)让RedisProperties
类被扫描到的关键。这时,如果RedisAutoConfiguration被扫描到,则同时也会去扫描RedisProperties类。 - @Import({ LettuceConnectionConfiguration.class,
JedisConnectionConfiguration.class })通过@Import注解方式生成类实例并注入Spring容器。
发现其会引入spring-data-redis,因此只有当我们在pom中引入spring-boot-starter-data-redis时,RedisAutoConfiguration才会真正的开启扫描。这也体现了Spring Boot的即插即用和方便快捷的自动配置。
然后下面还有一个io.lettuce,而之前在RedisAutoConfiguration中我们知道redisTemplate方法最终会把一个LettuceConnectionFactory实例注入Spring容器,而在这里实际上就已经大致表明了RedisAutoConfiguration会使用Lettuce客户端了。
总结
当要使用Spring Boot提供的redis客户端功能时,注入RedisTemplate的流程大致如下。
1.pom中引入spring-boot-starter-data-redis,并配置application.properties。
2.pom会根据spring-boot-starter-data-redis来引入spring-data-redis。
3.spring-data-redis中包含RedisOperations类。
4.启动Spring Boot,在refreshContext(context);中会初始化beanFactory,读取配置信息,初始化Spring容器,注入bean。因为**@EnableAutoConfiguration开启的关系,会读取配置中EnableAutoConfiguration相关的类,并实例化注入Spring 容器。
5.根据配置文件扫描到RedisAutoConfiguration**。当RedisOperations存在时RedisAutoConfiguration才会被扫描。
6.通过@EnableConfigurationProperties(RedisProperties.class)和@ConfigurationProperties(prefix = “spring.redis”),把application.properties中的对应属性进行绑定,并注入RedisProperties配置类。
7.RedisAutoConfiguration中的@Import会引入LettuceConnectionConfiguration和JedisConnectionConfiguration
8.LettuceConnectionConfiguration和JedisConnectionConfiguration被扫描,扫描到内部的**@Bean,使用上一步中注入的RedisProperties** bean作为参数来实例化LettuceConnectionFactory和JedisConnectionFactory,并以RedisConnectionFactory类注入Spring容器。
8.扫描并注入RedisAutoConfiguration类内的@Bean,其中会使用RedisConnectionFactory bean作参数实例化RedisTemplate。
9.将RedisTemplate实例注入。
10.然后就能通过引用RedisTemplate来操作redis了。