官网:https://spring.io/projects/spring-data-redis
Spring Data Redis是Spring Data系列的一部分,可轻松配置并从Spring应用程序访问Redis,并支持low-level和hight-level的抽象接口。可以看到spring data下面共提供了如下技术方案:
市面上已经有 Redis、Redisson、Lettuce 等优秀的 Java Redis 工具库,为什么还要有 Spring Data Redis 呢?
- 对于下层,Spring Data Redis 提供了统一的操作模板(RedisTemplate 类),封装了 Jedis、Lettuce 客户端库的 API 操作,来访问 Redis 数据(Spring Data Redis 底层使用的是 Jedis、Lettuce 库)。
- 对于上层,开发者学习如何使用 Spring Data Redis 即可,而无需关心 Jedis、Lettuce 的 API 操作。甚至,未来如果我们想将 Redis 访问从 Jedis 迁移成 Lettuce 来,无需做任何的变动。
- springboot 2.X 版本使用Lettce作为默认连接池,可以切换到jedis。
注意:目前Spring Data Redis 暂时只支持 Jedis、Lettuce 的内部封装,而 Redisson 是由 redisson-spring-data 来提供。
说明:jedis的使用见 https://blog.csdn.net/liuxiao723846/article/details/86724124
接下来,我们主要介绍spring-data-redis的使用(redis单机情况),对于redis cluster、redis sentinel的情况后续介绍。
1、单数据源(springboot)
1)maven:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependencies>
<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>
<exclusions>
<!--去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce作为Redis 客户端 -->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
说明:spring-boot-starter-data-redis中默认使用lettuce,若使用jedis库,需要将其exclude,然后手动引入jedis。
2)redis.properties:
redis.host=localhost
redis.port=7395
redis.password=123
redis.timeout=600000
# Redis 数据库号,默认为 0
redis.database=0
#最大空闲数
redis.maxIdle=300
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
#redis.maxActive=600
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
redis.maxTotal=1000
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
redis.maxWaitMillis=1000
#连接的最小空闲时间 默认1800000毫秒(30分钟)
redis.minEvictableIdleTimeMillis=300000
#每次释放连接的最大数目,默认3
redis.numTestsPerEvictionRun=1024
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
redis.timeBetweenEvictionRunsMillis=30000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true
#在空闲时检查有效性, 默认false
redis.testWhileIdle=true
3)RedisConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Value("${redis.password}")
private String pwd;
@Value("${redis.timeout}")
private Integer timeout;
@Value("${redis.maxIdle}")
private Integer maxIdle;
@Value("${redis.maxTotal}")
private Integer maxTotal;
@Value("${redis.maxWaitMillis}")
private Integer maxWaitMillis;
@Value("${redis.minEvictableIdleTimeMillis}")
private Integer minEvictableIdleTimeMillis;
@Value("${redis.numTestsPerEvictionRun}")
private Integer numTestsPerEvictionRun;
@Value("${redis.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMillis;
@Value("${redis.testOnBorrow}")
private boolean testOnBorrow;
@Value("${redis.testWhileIdle}")
private boolean testWhileIdle;
/**
* JedisPoolConfig 连接池
*/
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);// 最大空闲数
jedisPoolConfig.setMaxTotal(maxTotal);// 连接池的最大数据库连接数
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);// 最大建立连接等待时间
jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
jedisPoolConfig.setTestOnBorrow(testOnBorrow);//是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
jedisPoolConfig.setTestWhileIdle(testWhileIdle);//在空闲时检查有效性, 默认false
return jedisPoolConfig;
}
/**
* 链接工厂
*/
@Bean
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory();
JedisConnectionFactory.setPoolConfig(jedisPoolConfig);
JedisConnectionFactory.setHostName(host);
JedisConnectionFactory.setPort(port);
if (pwd != null && !pwd.equals("")) {
JedisConnectionFactory.setPassword(pwd);
}
JedisConnectionFactory.setTimeout(timeout);
return JedisConnectionFactory;
}
/**
* RedisTemplate
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式
// GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer();
// template.setValueSerializer(jacksonSerializer);// value采用jackson的序列化方式
// template.setHashValueSerializer(jacksonSerializer);// hash的value采用jackson序列化方式
template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(factory);// 设置链接工厂
return template;
}
}
说明:
- 如果代码中需要自己设置value的序列化方式,在RedisConfig中就无需指定。(比如:业务代码中有的按照json,有的按照pb序列化)
- spring-data-redis中字带了string、jackson等方式的序列化
注意:如果没有指定key的序列化方式,使用时发现在redis中的key会多出来类似\xac\xed\x00\x05t\x00这种字符串,所以,最好指定key的序列化方式为string。
说明:新的spring-data-redis中,上面的方式创建JedisConnectionFactory已经被废弃了,如下:
新的api中引入了RedisStandaloneConfiguration和JedisClientConfiguration,可以使用如下方式创建:
4)使用:
@Service
public class TestService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public String test1(String value) {
redisTemplate.opsForValue().set("test_1",value);
return (String)redisTemplate.opsForValue().get("test_1");
}
public String test2(String value) {
User user = new User(99,value);
byte[] serialize = SerializeUtil.serialize(user);
redisTemplate.opsForValue().set("test_9",serialize);
byte[] bytes = (byte[])redisTemplate.opsForValue().get("test_9");
User obj = (User) SerializeUtil.unserialize(bytes);
return obj.toString();
}
}
//序列化工具
public class SerializeUtil {
public static byte[] serialize(Object object) {//序列化
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
return bytes;
} catch (Exception e) {
System.out.println(e);
}
return null;
}
public static Object unserialize(byte[] bytes) {//反序列化
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
System.out.println(e);
}
return null;
}
}
2、单数据源2(spring)
上面使用的是注解方式(springboot默认采用config注解,来代替application.xml中各种bean的声明),接下来我们采用原始的spring application.xml配置的方式来实现一遍。
1)maven:
<properties>
<org.springframework.version>5.0.8.RELEASE</org.springframework.version>
</properties>
<dependencies>
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
<exclusions>
<exclusion><!-- 去掉对 Lettuce 的依赖-->
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
注:spring的版本要和spring-data-redis的版本对应,否则在启动应用时会报java.lang.NoSuchMethodError(spring应用报该类错误,一般来说都是版本冲突导致);其次,由于springmvc中依赖了jackson,所以jackson的版本也要对应。由于springboot简化了spring 项目的配置,所以我们可以在springboot项目中找到一份相匹配的spring、springmvc、spring-data-*、jackson等版本信息。
2)spring配置文件:
a、springMVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!-- <aop:aspectj-autoproxy/> -->
<!-- 基于Annotation的映射方式 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 自动搜索Sping的注解类 -->
<context:component-scan base-package="cn.edu.nuc.springmvc_test" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 配置静态资源,直接映射到对应的文件夹,不被DispatcherServlet处理 -->
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
b、application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd">
<task:annotation-driven/><!--开启spring scheduled注解-->
<!-- 自动搜索Sping的注解类-->
<context:component-scan base-package="cn.edu.nuc.springmvc_test" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- PropertyPlaceholderConfigurer -->
<context:property-placeholder location="classpath*:*.properties"/>
<import resource="redis.xml"/>
</beans>
c、redis.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--redis connection pool-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" primary="true">
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!-- redis connection factory -->
<bean id="redisConnectionFactory" primary="true" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<property name="poolConfig" ref="poolConfig"/>
</bean>
<bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- redis template -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
</beans>
注:多个spring配置文件中只能配置一个PropertyPlaceholderConfigurer,见:https://blog.csdn.net/liuxiao723846/article/details/116493944
说明:在spring项目中,使用了各种xml配置代替了springboot中的各种Config类。
3)redis.properties:
在classpath下创建redis.properties文件,同上。
4)测试类:同上
3、单数据源(springboot中使用xml配置文件)
上面分别介绍了在springboot和spring项目中使用spring-data-redis组件。都知道springboot是用来简化spring应用的一个框架,我们可以把spring项目中的redis.xml 放到springboot项目中,并且在main方法上通过@ImportResource注解来指定redis.xml配置文件,同时在项目中去掉对应的RedisConfig类。如下:
1)main方法:
@SpringBootApplication
@ImportResource(value = {"classpath:redis.xml"})
@ComponentScan(basePackages = {"cn.edu.nuc.springboot_test.*"})
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(App.class, args);
}
}
2)redis.xml文件:
把上面的redis.xml放到springboot项目的classpath下,在redis.xml中通过<context:property-placeholder>标签来指定redis.properties文件,同时将RedisConfig类删掉,即可。
4、多数据源
在实际项目中,一个应用往往会从多个redis数据源中获取数据,接下来,我们介绍如何在springboot项目中配置多个redis数据源。
4.1)springboot中通过spring-data-redis配置多个redis数据源:
1)RedisConfig.java:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfig {
//数据源1
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Value("${redis.password}")
private String pwd;
@Value("${redis.timeout}")
private Integer timeout;
//数据源2
@Value("${demo.redis.host}")
private String demoHost;
@Value("${demo.redis.port}")
private Integer demoPort;
@Value("${demo.redis.password}")
private String demoPwd;
@Value("${demo.redis.database}")
private Integer demoDatabase;
@Value("${demo.redis.timeout}")
private Integer demoTimeout;
//连接池配置信息
@Value("${redis.maxIdle}")
private Integer maxIdle;
@Value("${redis.maxTotal}")
private Integer maxTotal;
@Value("${redis.maxWaitMillis}")
private Integer maxWaitMillis;
@Value("${redis.minEvictableIdleTimeMillis}")
private Integer minEvictableIdleTimeMillis;
@Value("${redis.numTestsPerEvictionRun}")
private Integer numTestsPerEvictionRun;
@Value("${redis.timeBetweenEvictionRunsMillis}")
private long timeBetweenEvictionRunsMillis;
@Value("${redis.testOnBorrow}")
private boolean testOnBorrow;
@Value("${redis.testWhileIdle}")
private boolean testWhileIdle;
//JedisPoolConfig 连接池
@Bean(name="jedisPoolConfig")
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);// 最大空闲数
jedisPoolConfig.setMaxTotal(maxTotal);// 连接池的最大数据库连接数
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);// 最大建立连接等待时间
jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
jedisPoolConfig.setTestOnBorrow(testOnBorrow);//是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
jedisPoolConfig.setTestWhileIdle(testWhileIdle);//在空闲时检查有效性, 默认false
return jedisPoolConfig;
}
//数据源1的链接工厂
@Bean(name="jedisConnectionFactory")
@Primary
public JedisConnectionFactory jedisConnectionFactory(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
JedisConnectionFactory JedisConnectionFactory = new JedisConnectionFactory();
JedisConnectionFactory.setPoolConfig(jedisPoolConfig);
JedisConnectionFactory.setHostName(host);
JedisConnectionFactory.setPort(port);
if (pwd != null && !pwd.equals("")) {
JedisConnectionFactory.setPassword(pwd);
}
JedisConnectionFactory.setTimeout(timeout);
return JedisConnectionFactory;
}
//数据源2的连接工厂
@Bean(name="demoJedisConnectionFactory")
public JedisConnectionFactory demoJedisConnectionFactory(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig){
//设置host、port等
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(demoHost);
redisConfig.setPort(demoPort);
if (demoPwd != null && !demoPwd.equals("")) {
redisConfig.setPassword(RedisPassword.of(demoPwd));
}
redisConfig.setDatabase(demoDatabase);
//设置连接池、timeout等
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofMillis(demoTimeout))
.usePooling().poolConfig(jedisPoolConfig).build();
return new JedisConnectionFactory(redisConfig,jedisClientConfiguration);
}
//数据源1的redisTemplate
@Bean(name="redisTemplate")
public RedisTemplate<String, Object> redisTemplate(@Qualifier("jedisConnectionFactory") RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式
template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(factory);// 设置链接工厂
return template;
}
//数据源2的redisTemplate
@Bean(name="demoRedisTemplate")
public RedisTemplate<String, Object> demoRedisTemplate(@Qualifier("demoJedisConnectionFactory") RedisConnectionFactory demoJedisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);// key采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);// hash的key采用String的序列化方式
template.setEnableTransactionSupport(true);// 开启事务
template.setConnectionFactory(demoJedisConnectionFactory);// 设置链接工厂
return template;
}
}
说明:每个数据源都配置一个连接工厂。在配置JedisConnectionFactory bean时,需要指定primary,否则会报如下错误:
Parameter 0 of method redisTemplate in org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration required a single bean, but 2 were found:
2)使用:
@Service
public class TestService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;//数据源1
@Autowired
private RedisTemplate<String, Object> demoRedisTemplate;//数据源 2
4.2)源码解读:
通过spring-data-redis使用redis,通常需要配置以下三个bean:
- JedisPoolConfig:连接池相关的配置;
- JedisConnectionFactory:redis连接工厂,依赖JedisPoolConfig对象;
- RedisTemplate:redis模版,依赖JedisConnectionFactory对象;
我们知道,在spring中默认bean都是单例(除非指定bean的Scope="prototype"),在上面的多数据源配置中,JedisPoolConfig bean只配置了一个,每个数据源都配置了一个JedisConnectionFactory bean,这样会不会造成不同数据源的连接工厂使用的是一个连接池对象?先给答案:不会!!!
启动上面多数据源的应用,使用JvisualVM连接上去,通过MBean可以看到,系统中一共有两个对象池,这说明每个redis数据源的连接工厂使用了一个连接池:
通过源码发现:
前面我们知道,spring-data-redis底层使用的是jedis或者lettuce库,这两个库都提供了redis连接池的功能。这里我们一jedis为例子:
在JedisConnectionFactory中有一个redis.clients.util.Pool pool 属性,这个属性正是jedis库提供的连接池对象(底层是org.apach.commons.pool2),该属性的初始化是通过如下方法完成的:
从这里可以清晰的看到,我们配置的JedisPoolConfig bean只是保存了连接池相关的信息,在JedisConnectionFactory中会根据这个信息来真正创建redis的连接池。所以,JedisPoolConfig bean 是单例还是多实例,都无所谓!
4.3)xml版本的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath*:redis.properties" ignore-unresolvable="true" />
<!-- 连接池信息 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" primary="true">
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!-- 数据源1 factory -->
<bean id="redisConnectionFactory" primary="true" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.password}"/>
<!--<property name="database" value="${redis.database}"/>-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
<!-- 数据源2 factory -->
<bean id="demoRedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${demo.redis.host}"/>
<property name="port" value="${demo.redis.port}"/>
<property name="password" value="${demo.redis.password}"/>
<!--<property name="database" value="${demo.redis.database}"/>-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
<bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- 数据源1 template -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
<!-- 数据源2 template -->
<bean id="demoRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="demoRedisConnectionFactory"/>
<property name="keySerializer" ref="stringReadisSerializer"/>
</bean>
</beans>
注意:在springboot中,多个数据源的JedisConnectionFactory bean需要配置primary,在springmvc项目中,就不需要指定primary。