1.前言
本文为大家提供一个 redis 配置多数据源的实现方案,在实际项目中遇到,分享给大家。后续如果有时间会写一个升级版本,升级方向在第5点。
2.git 示例地址
git 仓库地址:https://github.com/huajiexiewenfeng/redis-multi-spring/tree/master/redis-multi-spring
不同的 spring boot 版本,对应的写法可能有小的区别,但思想不变
            
            
              xml
              
              
            
          
                  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>3.需求分析
对于 redis 配置多数据源的需求,至少可以分析出以下需求和实现点
- 在同一个项目中,可以操作多个 redis 服务实例
- 通过不同的 redisTemplate 可以调用不同的 redis 实例服务
- Spring @Qualifier 来实现
 
- 如果原项目已经引入spring-redis,不能影响 Spring boot 里面默认配置的 redis 自动配置服务
- org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
- 要与 RedisAutoConfiguration 里面的 spring bean 完全隔离
 
- 因为 host port 等重要参数都在 RedisConnectionFactory 中,所以 RedisConnectionFactory 我们需要配置多个
4.代码实现
不能影响 Spring boot 里面默认配置的 redis 自动配置服务,以 JedisConnectionConfiguration 自动配置实现为例。
- @ConditionalOnMissingBean({RedisConnectionFactory.class}) 表示如果 spring 容器中存在 RedisConnectionFactory 的具体实现,那么该自动配置 bean 方法不会生效;所以在我们后面的实现中要避免这种写法。

4.1 第一个 RedisTemplate 配置
需要特别注意的是,在某些 spring boot 版本里面,如果不写或者少写该配置,使用过程中会报错
- template.afterPropertiesSet();
- jedisConnectionFactory.afterPropertiesSet();
为了避免 getJedisConnectionFactory 方法影响 spring redis 默认配置,不能增加 @Bean 注解的方式,用 name 属性区分也不行。
4.1.1 RedisOneProperties Properties 类
            
            
              java
              
              
            
          
          @Configuration
@ConfigurationProperties(prefix = "multi.redis.one")
@Data
public class RedisOneProperties {
    private String host;
    private int port;
    private int database = 1;
    private String password;
}4.1.2 RedisMultiConfiguration 具体配置
            
            
              java
              
              
            
          
              @Autowired
    private RedisOneProperties properties;
    // 第一个Redis服务器的配置
    @Bean("oneRedisTemplate")
    public RedisTemplate<String, Object> oneRedisTemplate() {
        JedisConnectionFactory jedisConnectionFactory = this.getJedisConnectionFactory();
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置key的序列化方式
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        template.setConnectionFactory(jedisConnectionFactory);
        template.setKeySerializer(keySerializer);
        // 设置value的序列化方式
        Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是无论什么都可以序列化
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 启用DefaultTyping,方便我们反序列化时知道对象的类型
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        valueSerializer.setObjectMapper(om);
        template.setValueSerializer(valueSerializer);
        // 设置Hash的key和value序列化方式
        template.setHashKeySerializer(keySerializer);
        template.setHashValueSerializer(valueSerializer);
        // 设置value的泛型类型,这样在存取的时候才会序列化和反序列化成设置的对象类型
        // 注意:这里只是设置了value的泛型,key还是String类型
        template.afterPropertiesSet();
        return template;
    }
    private JedisConnectionFactory getJedisConnectionFactory() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        // 设置连接池参数,例如最大连接数、最大空闲连接数等
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(30);
        poolConfig.setMinIdle(10);
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
        jedisConnectionFactory.setHostName(properties.getHost());
        jedisConnectionFactory.setPort(properties.getPort());
        jedisConnectionFactory.setDatabase(properties.getDatabase());
        jedisConnectionFactory.setPassword(properties.getPassword());
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }4.2 第二个 RedisTemplate 配置
以此类推
4.2.1 RedisTwoProperties Properties 类
            
            
              java
              
              
            
          
          @Configuration
@ConfigurationProperties(prefix = "multi.redis.two")
@Data
public class RedisTwoProperties {
    private String host;
    private int port;
    private int database = 1;
    private String password;
}4.2.2 RedisMultiConfiguration 具体配置
            
            
              java
              
              
            
          
              @Autowired
    private RedisTwoProperties propertiesTwo;
    @Bean("twoRedisTemplate")
    public RedisTemplate<String, Object> twoRedisTemplate() {
        JedisConnectionFactory jedisConnectionFactory = this.getJedisConnectionFactoryTwo();
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置key的序列化方式
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        template.setConnectionFactory(jedisConnectionFactory);
        template.setKeySerializer(keySerializer);
        // 设置value的序列化方式
        Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是无论什么都可以序列化
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 启用DefaultTyping,方便我们反序列化时知道对象的类型
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        valueSerializer.setObjectMapper(om);
        template.setValueSerializer(valueSerializer);
        // 设置Hash的key和value序列化方式
        template.setHashKeySerializer(keySerializer);
        template.setHashValueSerializer(valueSerializer);
        // 设置value的泛型类型,这样在存取的时候才会序列化和反序列化成设置的对象类型
        // 注意:这里只是设置了value的泛型,key还是String类型
        template.afterPropertiesSet();
        return template;
    }
    private JedisConnectionFactory getJedisConnectionFactoryTwo() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        // 设置连接池参数,例如最大连接数、最大空闲连接数等
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(30);
        poolConfig.setMinIdle(10);
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
        jedisConnectionFactory.setHostName(propertiesTwo.getHost());
        jedisConnectionFactory.setPort(propertiesTwo.getPort());
        jedisConnectionFactory.setDatabase(propertiesTwo.getDatabase());
        jedisConnectionFactory.setPassword(propertiesTwo.getPassword());
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }4.3 application.properties 配置文件
            
            
              properties
              
              
            
          
          spring.application.name=${APPLICATION_NAME:redis-multiple}
server.port=${SERVER_PORT:22216}
# spring 默认配置
spring.redis.database=${REDIS_DB_INDEX:1}
spring.redis.flushdb=${REDIS_FLUSHDB:false}
spring.redis.host=${REDIS_HOST:127.0.0.1}
spring.redis.port=${REDIS_PORT:6379}
spring.redis.password=123456
#第一个 redis 实例配置
multi.redis.one.database=${REDIS_DB_INDEX:2}
multi.redis.one.flushdb=${REDIS_FLUSHDB:false}
multi.redis.one.host=${REDIS_HOST:127.0.0.1}
multi.redis.one.port=${REDIS_PORT:6379}
multi.redis.one.password=123456
#第二个 redis 实例配置
multi.redis.two.database=${REDIS_DB_INDEX:3}
multi.redis.two.flushdb=${REDIS_FLUSHDB:false}
multi.redis.two.host=${REDIS_HOST:127.0.0.1}
multi.redis.two.port=${REDIS_PORT:6379}
multi.redis.two.password=1234564.4 测试 Demo
            
            
              java
              
              
            
          
          @RestController
public class TestController {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    @Qualifier("oneRedisTemplate")
    private RedisTemplate<String, Object> redisTemplateOne;
    @Autowired
    @Qualifier("twoRedisTemplate")
    private RedisTemplate<String, Object> redisTemplateTwo;
    @GetMapping("/test/redis/add")
    public void profileDetails() {
        redisTemplate.opsForValue().set("test1", "1");
        redisTemplateOne.opsForValue().set("test2", 2);
        redisTemplateTwo.opsForValue().set("test3", 3);
    }
}浏览器输入
            
            
              html
              
              
            
          
          http://127.0.0.1:22216/test/redis/add执行结果如下:
db1

db2

db3

5.版本升级想法
1.不需要每一个配置都写一个 Properties 类
2.不需要每一个配置都写一个 RedisTemplate 配置
            
            
              java
              
              
            
          
              @Bean("oneRedisTemplate")
    public RedisTemplate<String, Object> oneRedisTemplate() {...}
    @Bean("twoRedisTemplate")
    public RedisTemplate<String, Object> twoRedisTemplate() {...}3.使用方法可以与 properties 里面的配置直接对应,比如 oneRedisTemplate
            
            
              java
              
              
            
          
              @Autowired
    @Qualifier("oneRedisTemplate")
    private RedisTemplate<String, Object> redisTemplateOne;对应 application.properties 中的 multi.redis.one 前缀
            
            
              properties
              
              
            
          
          multi.redis.one.database=${REDIS_DB_INDEX:2}
multi.redis.one.flushdb=${REDIS_FLUSHDB:false}
multi.redis.one.host=${REDIS_HOST:127.0.0.1}
multi.redis.one.port=${REDIS_PORT:6379}
multi.redis.one.password=123456如果有时间,会在下篇文章中实现。