Spring Data Redis 多源
完整代码:Ciiiiing/springboot_multi_redis
最近需要在同一个项目中访问多个 redis
而 spring 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 类
那么那这个注解就是说
RedisAutoConfiguration
这个类是个配置类,用于定义一个或多个 beanclasspath
下存在RedisOperations.class
这个类则加载它- 启用
RedisProperties.class
该配置类 - 导入配置类
LettuceConnectionConfiguration.class
和JedisConnectionConfiguration.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,那我们来理解一下是什么意思
- 表明
redisTemplate
这个方法用于生成一个由spring
管理的bean
- 若没有
redisTemplate
这个bean
则加载redisTemplate
- 若容器中只有一个
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 多源相关推荐
- Spring Data Redis—Pub/Sub(附Web项目源码)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring Data Redis:Sentinel的高可用性
1.概述 为了使Redis具有高可用性,我们可以使用Spring Data Redis对Redis Sentinel的支持. 借助Sentinel,我们可以创建自动抵御某些故障的Redis部署. Re ...
- spring mvc Spring Data Redis RedisTemplate [转]
http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...
- Spring Data Redis 实践
前言 Spring Data Redis是Spring Data大家族的一部分,提供了基于spring应用的简易配置与redis服务访问,它为存储与交互提供了低级(low-level)和高级的(hig ...
- Spring Data Redis学海拾贝
简介: Redis redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写.企业开发通常采用Redis来实现缓存.同类的产品还有memcache .memcached .Mo ...
- spring data redis 配置
参考:http://www.linuxidc.com/Linux/2015-04/116798.htm 纠结了半天,最终还是放弃使用spring data redis.因为它好像是不支持读写分离的. ...
- Spring Data Redis存储库
8. Redis存储库 使用Redis存储库允许在Redis哈希中无缝地转换和存储域对象,应用自定义映射策略并利用二级索引. Redis存储库至少需要Redis Server 2.8.0版. 8.1. ...
- Spring data redis 异常
2019独角兽企业重金招聘Python工程师标准>>> spring 集成 spring-data-redis 版本: spring低版本 + spring-data-redis 高 ...
- 使用Spring Data Redis操作Redis(集群版)
说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点.并且会与一些低版本的Sp ...
最新文章
- 【Android APT】注解处理器 ( Element 注解节点相关操作 )
- wxWidgets:wxSplitterWindow概述
- C#GDI画圆及填充
- Assign the task HDU - 3974(线段树+dfs建树+单点查询+区间修改)
- 每日一题(49)—— 有符号数与无符号数
- Java中使用开源库JSoup解析HTML文件实例
- [Swift]LeetCode862. 和至少为 K 的最短子数组 | Shortest Subarray with Sum at Least K
- 深入浅出Symfony2 - 结合MongoDB开发LBS应用
- vscode开发python使用教程_VSCode 支持Python
- 软件工程题库(荟萃)
- Yanobox Moods for mac(FCPX/AE/PR滤镜插件)激活版
- 最新投影圣经投影赞美诗歌圣经诗歌2020
- 计算机操作系统|汤小丹|第四版|习题答案(五)
- LM358资料及引脚图
- uniapp对接极光推送
- 关于WORD提示“Word无法创建工作文件,请检查临时环境变量”问题的解决办法
- 科学万能科计算机科学万能计算机,万能科学计算器CalcES v5.0.5脱壳专业会员版...
- 【导数术】4.三次函数
- Centos7 切换为163 网易yum
- linux 多线程 semaphore ,Linux下多线程编程-Pthread和Semaphore使用.doc