【实用技巧】【SpringBoot + Redis】SpringBoot 启动时,注入多个RedisTemplate

发布时间 2023-08-11 08:09:37作者: 酷酷-

1  前言

大家可能都用过缓存 Redis,有一个这样的场景比如商品中心有商品的缓存,渠道中心有渠道信息的缓存,应用端去获取这些信息的时候,我是这么想的能不能直接在应用这里把多个中心下的 Redis,我也注入进来,也就是注入多个中心的 RedisTemplate,这样应用可以先去缓存中拿,类似这样的场景哈,就是在启动的时候,注入多个RedisTemplate,来获取信息。

2  实现

首先考虑一个过程就是我 SpringBoot 在启动的时候通过读取配置文件的信息,来注入多个RedisTemplate。我这里直接上代码了哈:(连接工厂用的 LettuceConnectionFactory)

比如我这里有配置:

mul:
  redis:
    # 核心 redis
    core: 15
    # 渠道 redis
    channel: 14
    # 商品 redis
    item: 13

启动的配置类:

package com.kuku.core.sys.config;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Iterator;
import java.util.Map;

/**
 * @author kuku
 * @description
 */
@Slf4j
@Component
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class MultiRedisConfig implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    @Autowired
    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        // 环境变量引进来 用于获取配置
        this.environment = environment;
    }

    // 多redis配置的前缀
    private static final String REDIS_PREFIX = "mul.redis.";
    private static final String REDIS_BEAN_NAME_SUFFIX = "RedisTemplate";

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        AbstractEnvironment standardServletEnvironment = (AbstractEnvironment) environment;
        // 筛选redis的配置,然后筛选出 mul.redis.的配置 进行注入
        Iterator<PropertySource<?>> iterator = standardServletEnvironment.getPropertySources().iterator();
        Map<String, Integer> redisMap = Maps.newHashMapWithExpectedSize(20);
        while (iterator.hasNext()) {
            PropertySource<?> source = iterator.next();
            String name = source.getName();
            // 从redis配置中获取配置信息
            if (!name.contains("redis")) {
                continue;
            }
            Object o = source.getSource();
            if (o instanceof Map) {
                for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
                    String key = entry.getKey();
                    if (key.startsWith(REDIS_PREFIX)) {
                        redisMap.put(key.replace(REDIS_PREFIX, "") + REDIS_BEAN_NAME_SUFFIX, Integer.parseInt(entry.getValue().toString()));
                    }
                }
            }
        }
        // 依次注入
        if (!CollectionUtils.isEmpty(redisMap)) {
            redisMap.forEach((key, val) -> {
                log.info("--init redis template {}, database index {}", key, val);
                beanDefinitionRegistry.registerBeanDefinition(key, generateRedisTemplateDefinition(val));
            });
        }
    }

    // 生成 Bean 的定义信息
    public GenericBeanDefinition generateRedisTemplateDefinition(int database) {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(RedisTemplate.class);
        // 添加属性配置 比如连接工厂(配置连接池)、默认的序列化
        addPropertyValues(definition, database);
        return definition;
    }

    public void addPropertyValues(GenericBeanDefinition definition, int database) {
        RedisConnectionFactory redisConnectionFactory = getRedisConnectionFactory(database);
        definition.getPropertyValues().add("connectionFactory", redisConnectionFactory);
        // 默认序列化器 默认都采用 fastjson 来处理 key value
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        definition.getPropertyValues().add("defaultSerializer", fastJsonRedisSerializer);
    }


    /**
     * 是负责建立Factory的连接工厂类
     * @param database
     * @return
     */
    public RedisConnectionFactory getRedisConnectionFactory(int database) {
        // 基础配置信息
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(environment.getProperty("spring.redis.host", "localhost"));
        redisStandaloneConfiguration.setPort(Integer.parseInt(environment.getProperty("spring.redis.port", "6379")));
        redisStandaloneConfiguration.setPassword(environment.getProperty("spring.redis.password", ""));
        redisStandaloneConfiguration.setDatabase(database);

        // 连接池信息
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(Integer.parseInt(environment.getProperty("spring.redis.max-total", "200")));
        poolConfig.setMinIdle(Integer.parseInt(environment.getProperty("spring.redis.min-idle", "50")));
        poolConfig.setMaxIdle(Integer.parseInt(environment.getProperty("spring.redis.max-idle", "150")));
        LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();

        // 创建
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfiguration);
        lettuceConnectionFactory.afterPropertiesSet();
        return lettuceConnectionFactory;
    }


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }


}

启动的效果:

实际使用:

测试效果:

3  小结

就到这里哈,就是类似这样的需要注入多个 RedisTemplate的,有写的不对的地方欢迎指正哈。