完整代码:Ciiiiing/springboot_multi_redis

最近需要在同一个项目中访问多个 redisspring data redis 默认是只支持一个数据源的,那就需要我们自己改造

网上搜了一些文章,大多有一些错漏,并且只给出了结果(还是错的)没有为什么,所以自己研究了一下,分享一下过程

首先在一个 spring boot 项目的 pom 文件中引入 spring data redis

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

yml 做如下配置,host 换成你自己的 redis 服务器地址

spring:redis:host: 192.168.253.132port: 6379database: 0

这样我们就可以使用 spring data redis 提供的 bean 来访问 redis 了,我们不妨先来试一试

@SpringBootApplication
public class SpringbootMultiRedisApplication implements CommandLineRunner {private final StringRedisTemplate stringRedisTemplate;@Autowiredpublic SpringbootMultiRedisApplication(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public static void main(String[] args) {SpringApplication.run(SpringbootMultiRedisApplication.class, args);}@Overridepublic void run(String... args) throws Exception {stringRedisTemplate.opsForValue().set("test", "1");}
}

先用 RDM 看看数据

运行一下我们的程序

好的,成功插入了,至此我们已经成功集成了单源 redis 接下来我们看看怎么实现多源

我们要知道 spring boot 之所以能完成自动配置是因为有很多 autoConfiguration 类来帮我们做了初始化,那我们就来搜一搜 redis 的自动配置类

ok,我们已经发现确实存在 redis 的自动配置类,我们进入看看

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate")@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}}

嗯,我们能看到 redis 的自动配置类还是比较简单的,大部分功能基本都靠注解来完成,那我们就一个个的来看看这些注解的作用

第一个 @Configuration,点进去就能看到官方对他的说明

Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime

表示一个类声明了一个或多个 @Bean 方法,并可由 Spring 容器处理,以便在运行时为这些 Bean 生成 Bean 定义和服务请求

第二个 @ConditionalOnClass

@Conditional that only matches when the specified classes are on the classpath.

在指定的类存在于 classpath 时加载

第三个 @EnableConfigurationProperties

Enable support for @ConfigurationProperties annotated beans. @ConfigurationProperties beans can be registered in the standard way (for example using @Bean methods) or, for convenience, can be specified directly on this annotation.

启用对 @ConfigurationProperties 注释的 bean 的支持。@ConfigurationProperties beans 可以以标准方式注册(例如使用 @Bean 方法),或者为了方便,可以直接在这个注解上指定

第四个 @Import

Indicates one or more component classes to import — typically @Configuration classes.

表示要导入的一个或多个组件类 – 通常是 @Configuration 类

那么那这个注解就是说

  1. RedisAutoConfiguration 这个类是个配置类,用于定义一个或多个 bean
  2. classpath 下存在 RedisOperations.class 这个类则加载它
  3. 启用 RedisProperties.class 该配置类
  4. 导入配置类 LettuceConnectionConfiguration.classJedisConnectionConfiguration.class

点进 RedisProperties.class 这里类里边看看

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {...
}

就是这个类指定了我们配置文件的格式(我们之前写的 yml

继续看剩下的注解

 @Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}

第一个 @Bean

Indicates that a method produces a bean to be managed by the Spring container

表明这个方法用于生产一个归 spring 管理的 bean 对象

第二个 @ConditionalOnMissingBean

@Conditional that only matches when no beans meeting the specified requirements are already contained in the BeanFactory. None of the requirements must be met for the condition to match and the requirements do not have to be met by the same bean.

@Conditional,只有在 BeanFactory 中已经没有满足指定要求的 Bean 时才会匹配。条件必须不满足任何要求才能匹配,而且这些要求不一定要由同一个 Bean 来满足

第三个 @ConditionalOnSingleCandidate

@Conditional that only matches when a bean of the specified class is already contained in the BeanFactory and a single candidate can be determined.

@Conditional,只有在 BeanFactory 中已经包含了指定类别的 Bean 并且可以确定一个候选者时才会匹配

The condition will also match if multiple matching bean instances are already contained in the BeanFactory but a primary candidate has been defined;

如果 BeanFactory 中已经包含了多个匹配的 Bean 实例,但已经定义了一个 primary(主要) 的候选者,那么该条件也将匹配

ok,那我们来理解一下是什么意思

  1. 表明 redisTemplate 这个方法用于生成一个由 spring 管理的 bean
  2. 若没有 redisTemplate 这个 bean 则加载 redisTemplate
  3. 若容器中只有一个 RedisConnectionFactory.class

好的,总体梳理一下,我们之所以只需要在 yml 文件中写好 redis 地址就能注入一个 StringRedisTemplate 对象来直接使用,是因为 RedisAutoConfiguration 这个自动配置类帮我们做了初始化

流程大概清楚了,该如何自定义数据源呢?

再看看这段代码

 @Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}

注意到 RedisConnectionFactory 根据命名我们猜测这是一个工厂类,回忆一下设计模式,工厂模式 用于初始化复杂的对象,ok 那应该就是在这里做了初始化,那么这个工厂类又是哪来的呢?

还记得之前的配置类吗?

@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })

我们使用的是 Lettuce 作为客户端,所以去看看 LettuceConnectionConfiguration.class

    @Bean@ConditionalOnMissingBean(RedisConnectionFactory.class)LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,ClientResources clientResources) {LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,getProperties().getLettuce().getPool());return createLettuceConnectionFactory(clientConfig);}

那么能看到是这里注入了 redisConnectionFactory,跟进这个方法

 private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {if (getSentinelConfig() != null) {return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);}if (getClusterConfiguration() != null) {return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);}return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);}

我们使用的是单点 redis 所以再看看 getStandaloneConfig() 这个方法

 protected final RedisStandaloneConfiguration getStandaloneConfig() {RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();if (StringUtils.hasText(this.properties.getUrl())) {ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());config.setHostName(connectionInfo.getHostName());config.setPort(connectionInfo.getPort());config.setUsername(connectionInfo.getUsername());config.setPassword(RedisPassword.of(connectionInfo.getPassword()));}else {config.setHostName(this.properties.getHost());config.setPort(this.properties.getPort());config.setUsername(this.properties.getUsername());config.setPassword(RedisPassword.of(this.properties.getPassword()));}config.setDatabase(this.properties.getDatabase());return config;}

nice,可以看到实在这里读取了配置文件

我们只要仿照这个建立自己的工厂类就可以了,让我们动手试试

建立一个 RedisConfig 来创建我们自己的工厂

@Configuration
public class RedisConfig {@Beanpublic LettuceConnectionFactory firstFactory() {RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("192.168.253.132", 6379);configuration.setDatabase(1);return new LettuceConnectionFactory(configuration);}@BeanStringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}
}

先建立一个工厂,给 db1 写入数据试试看

好的没有问题,我们接下来建立第二个工厂并指定给 db2 插入数据

@Configuration
public class RedisConfig {@Beanpublic LettuceConnectionFactory firstFactory() {RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("192.168.253.132", 6379);configuration.setDatabase(1);return new LettuceConnectionFactory(configuration);}@Beanpublic LettuceConnectionFactory secondFactory() {RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("192.168.253.132", 6379);configuration.setDatabase(2);return new LettuceConnectionFactory(configuration);}@BeanStringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}@BeanStringRedisTemplate stringRedisTemplate2(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}
}

好,我们运行看看

Parameter 0 of method stringRedisTemplate in com.example.springboot_multi_redis.config.RedisConfig required a single bean, but 2 were found:- firstFactory: defined by method 'firstFactory' in class path resource [com/example/springboot_multi_redis/config/RedisConfig.class]- secondFactory: defined by method 'secondFactory' in class path resource [com/example/springboot_multi_redis/config/RedisConfig.class]

很遗憾,我们看到程序报错了,这是为什么呢?

通过报错信息 required a single bean, but 2 were found 可以看到有一个地方需要一个 bean 但程序发现了两个不知道该使用哪一个,看到这里,你想起我们之前说的 @ConditionalOnSingleCandidate 这个注解了吗?从官方中我们知道,需要一个 primary 就可以了,嘿,这里我们用 @Primary 注解就好

    @Bean@Primarypublic LettuceConnectionFactory firstFactory() {RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration("192.168.253.132", 6379);configuration.setDatabase(1);return new LettuceConnectionFactory(configuration);}

再次运行程序(记得每次重新运行前清掉 redis 的内容,或者每次使用不同的 key 方便你判断是否成功)

ok,我们的代码是 stringRedisTemplate.opsForValue().set("test", "1"); 所以插入 db1 是符合预期的

现在我们想用 stringRedisTemplate2 来操作 db2,但细心的我们发现

    @BeanStringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}@BeanStringRedisTemplate stringRedisTemplate2(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}

这两段代码除了函数名以外完全一致,怎么能使用不同的工厂呢?

这就要用到另一个注解 @Qualifier

This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring.

用于在自动注入时指定使用的 bean,也就是 by type 的注入,@Autowired 默认是 by name

让我们加入这个注解

    @BeanStringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}@BeanStringRedisTemplate stringRedisTemplate2(@Qualifier("secondFactory") RedisConnectionFactory redisConnectionFactory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}

修改一下主类

@SpringBootApplication
public class SpringbootMultiRedisApplication implements CommandLineRunner {private final StringRedisTemplate stringRedisTemplate;private final StringRedisTemplate stringRedisTemplate2;@Autowiredpublic SpringbootMultiRedisApplication(StringRedisTemplate stringRedisTemplate,StringRedisTemplate stringRedisTemplate2) {this.stringRedisTemplate = stringRedisTemplate;this.stringRedisTemplate2 = stringRedisTemplate2;}public static void main(String[] args) {SpringApplication.run(SpringbootMultiRedisApplication.class, args);}@Overridepublic void run(String... args) throws Exception {stringRedisTemplate.opsForValue().set("test", "1");stringRedisTemplate2.opsForValue().set("test", "2");}
}

运行程序

好的,大功告成,我们可以通过同样的方法加入更多的 redis

这篇博客也就此结束了~~

祝大家学习愉快

Spring Data Redis 多源相关推荐

  1. Spring Data Redis—Pub/Sub(附Web项目源码)

    一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...

  2. Spring Data Redis:Sentinel的高可用性

    1.概述 为了使Redis具有高可用性,我们可以使用Spring Data Redis对Redis Sentinel的支持. 借助Sentinel,我们可以创建自动抵御某些故障的Redis部署. Re ...

  3. spring mvc Spring Data Redis RedisTemplate [转]

    http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...

  4. Spring Data Redis 实践

    前言 Spring Data Redis是Spring Data大家族的一部分,提供了基于spring应用的简易配置与redis服务访问,它为存储与交互提供了低级(low-level)和高级的(hig ...

  5. Spring Data Redis学海拾贝

    简介: Redis redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写.企业开发通常采用Redis来实现缓存.同类的产品还有memcache .memcached .Mo ...

  6. spring data redis 配置

    参考:http://www.linuxidc.com/Linux/2015-04/116798.htm 纠结了半天,最终还是放弃使用spring  data redis.因为它好像是不支持读写分离的. ...

  7. Spring Data Redis存储库

    8. Redis存储库 使用Redis存储库允许在Redis哈希中无缝地转换和存储域对象,应用自定义映射策略并利用二级索引. Redis存储库至少需要Redis Server 2.8.0版. 8.1. ...

  8. Spring data redis 异常

    2019独角兽企业重金招聘Python工程师标准>>> spring 集成 spring-data-redis 版本: spring低版本 + spring-data-redis 高 ...

  9. 使用Spring Data Redis操作Redis(集群版)

    说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...

最新文章

  1. 【Android APT】注解处理器 ( Element 注解节点相关操作 )
  2. wxWidgets:wxSplitterWindow概述
  3. C#GDI画圆及填充
  4. Assign the task HDU - 3974(线段树+dfs建树+单点查询+区间修改)
  5. 每日一题(49)—— 有符号数与无符号数
  6. Java中使用开源库JSoup解析HTML文件实例
  7. [Swift]LeetCode862. 和至少为 K 的最短子数组 | Shortest Subarray with Sum at Least K
  8. 深入浅出Symfony2 - 结合MongoDB开发LBS应用
  9. vscode开发python使用教程_VSCode 支持Python
  10. 软件工程题库(荟萃)
  11. Yanobox Moods for mac(FCPX/AE/PR滤镜插件)激活版
  12. 最新投影圣经投影赞美诗歌圣经诗歌2020
  13. 计算机操作系统|汤小丹|第四版|习题答案(五)
  14. LM358资料及引脚图
  15. uniapp对接极光推送
  16. 关于WORD提示“Word无法创建工作文件,请检查临时环境变量”问题的解决办法
  17. 科学万能科计算机科学万能计算机,万能科学计算器CalcES v5.0.5脱壳专业会员版...
  18. 【导数术】4.三次函数
  19. Centos7 切换为163 网易yum
  20. linux 多线程 semaphore ,Linux下多线程编程-Pthread和Semaphore使用.doc

热门文章

  1. JS unshift() 方法
  2. python怎么在图片上写字_python在图片上写汉字
  3. 基于 Linux 集群环境上 GPFS 的问题诊断
  4. Android Studio常用快捷键
  5. Android 设计模式之MVC,从一个实例中来理解MVC
  6. 3.堆栈指针寄存器 SP 详解
  7. 四步修改Linux ip地址
  8. android连iphone热点超时,Android19连接iOS13个人热点失败
  9. 传播模型——简单的元胞自动机(3)
  10. 虚拟机上的Linux系统如何联网?