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
.....
例子代碼:
我們在訪問接口時,打印 RedisTemplate
的 ConnectionFactory
即可知道使用的是哪個客戶端。
/**
* @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 客戶端:
啟動項目,訪問接口,可是我們看到,控制台打印出來的竟然還是 Lettuce
的 ConnectionFactory
。
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
會按照順序分別引入 LettuceConnectionConfiguration
和 JedisConnectionConfiguration
。而它們都會判斷 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