1、Spring Boot 2.x 的两种 Redis 客户端

首先,我们都知道,从 Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端。当然 Spring Boot 2.x 仍然支持 Jedis,并且你可以任意切换客户端。至于为什么会使用 Lettuce 替换 Jedis,大家可自行上网搜索。

2、我就是要使用 Jedis !

那么如果我们还是要在项目中使用 Jedis 作为 Redis 的客户端呢?是不是引入 Jedis 依赖即可?下面来试试。

引入依赖:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

配置:

可以加上下面的关于连接池配置,或者不加,因为有默认值。

spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.min-idle=1
.....

例子代码:

我们在访问接口时,打印 RedisTemplateConnectionFactory 即可知道使用的是哪个客户端。

/**
* @author Howinfun
* @desc
* @date 2020/1/15
*/
@RestController
public class TestController { @Autowired
private RedisTemplate redisTemplate; @GetMapping("/test")
public String test(){
System.out.println(redisTemplate.getConnectionFactory());
return "hello";
}
}

神奇的 Lettuce 客户端:

启动项目,访问接口,可是我们看到,控制台打印出来的竟然还是 LettuceConnectionFactory

org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@21891c74

3、源码分析

Redis 的自动配置类:

首先看到 RedisAutoConfiguration

@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
// 导入Lettuce和Jedis的连接配置类,而下面的创建RedisTemplate的RedisConnectionFactory在这两个配置类里都有生成。
@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;
}
}

Lettuce 的连接配置类:

然后到LettuceConnectionConfiguration

@Configuration
// 当存在RedisClient.class时执行。ps:RedisClient是Lettuce依赖里面的类
@ConditionalOnClass({RedisClient.class})
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
// .... 省略 @Bean
// 当Spring容器不存在RedisConnectionFactory类型的bean对象时执行
@ConditionalOnMissingBean({RedisConnectionFactory.class})
public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources) throws UnknownHostException { LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(clientResources, this.properties.getLettuce().getPool());
// 创建connectinFactory
return this.createLettuceConnectionFactory(clientConfig);
} // ..... 省略
}

Jedis 的连接配置类:

最后到JedisConnectionConfiguration

@Configuration
// 当同时存在GenericObjectPool.class, JedisConnection.class, Jedis.class时执行。ps:JedisConnection和Jedis都是Jedis依赖里面的类。GenericObjectPool是spring-boot-starter-data-redis里的类。
@ConditionalOnClass({GenericObjectPool.class, JedisConnection.class, Jedis.class})
class JedisConnectionConfiguration extends RedisConnectionConfiguration { // .... 省略 @Bean
// 当Spring容器不存在RedisConnectionFactory类型的bean对象时执行
@ConditionalOnMissingBean({RedisConnectionFactory.class})
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { // 创建connectinFactory
return this.createJedisConnectionFactory();
} // .... 省略
}

总结:

从上面的源码分析可得。首先 Redis 的自动化配置依靠的是 RedisAutoConfiguration ,而 RedisAutoConfiguration 会按照顺序分别引入 LettuceConnectionConfigurationJedisConnectionConfiguration 。而它们都会判断 Spring容器 中是否存在 ConnectionFactory ,不存在则创建。正是这个引入的顺序,导致 LettuceConnectionConfiguration 要先比 JedisConnectionConfiguration 执行,所以当 LettuceConnectionConfiguration 创建了 ConnectionFactory 后, JedisConnectionConfiguration 判断不为空而不继续创建了。所以即使我们引入了 Jedis 依赖,最后也还是使用 Lettuce 客户端。

ps: Spring Boot 2.x 使用 Lettuce 的原理:首先是依靠 @Import 的引入顺序,然后是 spring-boot-starter-data-redis 里有 Lettuce 的依赖,而没有 Jedis 的依赖 。

4、如何正确使用 Jedis 客户端

从上面的源码分析看到,我们有两种方案,不过前提都是先引入 Jedis 依赖。

第一:直接去掉 Lettuce 的依赖

使 LettuceConnectionConfiguration 不能生效,因为 @ConditionalOnClass({RedisClient.class}) 的不再成立,导致其不会生效执行。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>

第二:在启动类的 @SpringBootApplication 去掉 RedisAutoConfiguration ,然后自己自定义一个 RedisAutoConfiguration

在自定义的 RedisAutoConfiguration 中修改 @Import 关于两个客户端的 ConnectionConfiguration 的引入顺序即可。但是其实这样做很没必要,还不如直接去掉依赖。

@SpringBootApplication(exclude = RedisAutoConfiguration.class)

@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
// 先 Jedis 后 Lettuce
@Import({JedisConnectionConfiguration.class, LettuceConnectionConfiguration.class})
public class MyRedisAutoConfiguration {
public MyRedisAutoConfiguration() {
} // ...... 省略
}

最后

我们可以看到控制台打印的终于是 JedisConnectionFactory了。

org.springframework.data.redis.connection.jedis.JedisConnectionFactory@16c9834c

【Spring Boot 源码解读】之 【为何引入了 Jedis 依赖最后用的还是 Lettuce 客户端?】的更多相关文章

  1. Spring Boot源码(四):Bean装配

    为了演示Spring中对象是如何创建并放到spring容器中,这里新建一个maven项目: 其中pom.xm文件中只引入了一个依赖: <dependencies> <dependen ...

  2. 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  3. 曹工说Spring Boot源码(28)-- Spring的component-scan机制,让你自己来进行简单实现,怎么办

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  4. 精尽Spring Boot源码分析 - 序言

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  5. 精尽Spring Boot源码分析 - Jar 包的启动实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  6. 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  7. 精尽Spring Boot源码分析 - 支持外部 Tomcat 容器的实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  8. 精尽Spring Boot源码分析 - 剖析 @SpringBootApplication 注解

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  9. 精尽Spring Boot源码分析 - Condition 接口的扩展

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

随机推荐

  1. mysql 第二课 DML操作

    DML 数据操纵语句:INSERT UPDATE DELETE SELECT 主要用来对数据库的数据进行一些操作; DCL 数据定义语句:CREATE ALTER DROP  主要是用在定义或改变表的 ...

  2. mybatis查询无结果, 数据库运行相同sql查询出结果

    一.问题描述 mybatis查询无结果, 数据库运行相同sql查询出结果, 如下 这是数据库记录 这是mybatis查询出的结果, 记录条数0 这是直接将控制台一模一样的sql查询语句放到Navica ...

  3. Roslyn 使用 WriteLinesToFile 解决参数过长无法传入

    在写 Roslyn 的时候,经常需要辅助编译的工具,而这些工具需要传入一些参数,在项目很大的时候,会发现自己传入的参数比微软限制控制台可以传入的参数大很多,这时就无法传入了参数 本文告诉大家如何使用 ...

  4. H3C DHCP服务器显示及维护

  5. NuGet 符号服务器

    在新的 VisualStudio 支持使用 NuGet 符号服务器,可以支持新的 Portable PDB 调试符号的库,本文告诉大家如何打包上传带符号的库和使用符号服务器 在 2018 的 11 月 ...

  6. javascript中的深拷贝与浅拷贝

    javascript中的深拷贝与浅拷贝 基础概念 在了解深拷贝与浅拷贝的时候需要先了解一些基础知识 核心知识点之 堆与栈 栈(stack)为自动分配的内存空间,它由系统自动释放: 堆(heap)则是动 ...

  7. C# 线程参数

    . class ThreadSample { private readonly int _iterations; public ThreadSample(int iterations) { _iter ...

  8. Linux USB 的 Urbs

    linux 内核中的 USB 代码和所有的 USB 设备通讯使用称为 urb 的东西( USB request block). 这个请求块用 struct urb 结构描述并且可在 include/l ...

  9. ansible核心模块playbook介绍

    ansible的playbook采用yaml语法,它简单地实现了json格式的事件描述.yaml之于json就像markdown之于html一样,极度简化了json的书写.在学习ansible pla ...

  10. Asp.NetCore3.1版本的CodeFirst与经典的三层架构与AutoFac批量注入

    Core3.1 CodeFirst与AutoFac批量注入(最下面附GitHub完整 Demo,由于上传网速较慢,这里就直接压缩打包上传了) ===Core3.1 CodeFirst 数据库为远程阿里 ...