Springboot动态redis 数据源数据库
1, 准备工作,新建一个一个springboot maven 工程, 这里我们不需要web的依赖,只需要data-redis 的依赖就行的。
2, 代码逻辑
3, 正式的写代码
大部分的代码都和之前那个老哥文章代码差不多, 这里我只是加上了切换redis 数据库的逻辑。 核心代码
package com.ducheng.multi.redis;
import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.dao.DataAccessException;import org.springframework.data.redis.connection.*;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.util.ObjectUtils;import java.util.Map;
public class MultiRedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory, ReactiveRedisConnectionFactory { private final Map<String, LettuceConnectionFactory> connectionFactoryMap;
/**
* * 当前redis的名字
* */
private static final ThreadLocal<String> currentRedisName = new ThreadLocal<>();
/**
* 当前redis的db数据库
*/
private static final ThreadLocal<Integer> currentRedisDb = new ThreadLocal<>();
public MultiRedisConnectionFactory(Map<String, LettuceConnectionFactory> connectionFactoryMap) {
this.connectionFactoryMap = connectionFactoryMap;
}
public void setCurrentRedis(String currentRedisName) {
if (!connectionFactoryMap.containsKey(currentRedisName)) {
throw new RuntimeException("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration");
}
MultiRedisConnectionFactory.currentRedisName.set(currentRedisName);
}
/** * 选择连接和数据库
* @param currentRedisName
* @param db */
public void setCurrentRedis(String currentRedisName,Integer db) {
if (!connectionFactoryMap.containsKey(currentRedisName)) {
throw new RuntimeException("invalid currentRedis: " + currentRedisName + ", it does not exists in configuration");
}
MultiRedisConnectionFactory.currentRedisName.set(currentRedisName); MultiRedisConnectionFactory.currentRedisDb.set(db);
}
@Override
public void destroy() throws Exception {
connectionFactoryMap.values().forEach(LettuceConnectionFactory::destroy); }
@Override
public void afterPropertiesSet() throws Exception {
connectionFactoryMap.values().forEach(LettuceConnectionFactory::afterPropertiesSet);
}
private LettuceConnectionFactory currentLettuceConnectionFactory() {
String currentRedisName = MultiRedisConnectionFactory.currentRedisName.get();
if (!ObjectUtils.isEmpty(currentRedisName)) {
MultiRedisConnectionFactory.currentRedisName.remove();
return connectionFactoryMap.get(currentRedisName);
}
return connectionFactoryMap.get(MultiRedisProperties.DEFAULT);
}
@Override
public ReactiveRedisConnection getReactiveConnection() {
return currentLettuceConnectionFactory().getReactiveConnection();
}
@Override
public ReactiveRedisClusterConnection getReactiveClusterConnection() {
return currentLettuceConnectionFactory().getReactiveClusterConnection();
}
@Override
public RedisConnection getConnection() {
// 这里就是切换数据库的地方
Integer currentRedisDb = MultiRedisConnectionFactory.currentRedisDb.get();
if (!ObjectUtils.isEmpty(currentRedisDb)) {
LettuceConnectionFactory lettuceConnectionFactory = currentLettuceConnectionFactory();
lettuceConnectionFactory.setShareNativeConnection(false);
RedisConnection connection = lettuceConnectionFactory.getConnection();
connection.select(currentRedisDb);
return connection;
}
return currentLettuceConnectionFactory().getConnection();
}
@Override
public RedisClusterConnection getClusterConnection() {
return currentLettuceConnectionFactory().getClusterConnection();
}
@Override
public boolean getConvertPipelineAndTxResults() {
return currentLettuceConnectionFactory().getConvertPipelineAndTxResults(); }
@Override
public RedisSentinelConnection getSentinelConnection() {
return currentLettuceConnectionFactory().getSentinelConnection();
}
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return currentLettuceConnectionFactory().translateExceptionIfPossible(ex);
}
}
根据条件注解注入redis 数据库的工厂 核心代码
package org.springframework.boot.autoconfigure.data.redis;
import com.ducheng.multi.redis.MultiRedisConnectionFactory;import com.ducheng.multi.redis.MultiRedisProperties;import io.lettuce.core.resource.ClientResources;import org.springframework.beans.factory.ObjectProvider;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisClusterConfiguration;import org.springframework.data.redis.connection.RedisSentinelConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import java.util.HashMap;import java.util.Map;
@ConditionalOnProperty(prefix = "spring.redis", value = "enable-multi", matchIfMissing = false)@Configuration(proxyBeanMethods = false)public class RedisCustomizedConfiguration {
/** * @param builderCustomizers
* @param clientResources
* @param multiRedisProperties
* @return * @see org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration */
@Bean
public MultiRedisConnectionFactory multiRedisConnectionFactory( ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources,
MultiRedisProperties multiRedisProperties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider)
{
Map<String, LettuceConnectionFactory> connectionFactoryMap = new HashMap<>();
Map<String, RedisProperties> multi = multiRedisProperties.getMulti();
multi.forEach((k, v) -> {
LettuceConnectionConfiguration lettuceConnectionConfiguration = new LettuceConnectionConfiguration( v, sentinelConfigurationProvider, clusterConfigurationProvider );
LettuceConnectionFactory lettuceConnectionFactory = lettuceConnectionConfiguration.redisConnectionFactory(builderCustomizers, clientResources);
connectionFactoryMap.put(k, lettuceConnectionFactory);
});
return new MultiRedisConnectionFactory(connectionFactoryMap);
}
}
redis 的配置类
@ConfigurationProperties(prefix = "spring.redis")public class MultiRedisProperties {
/** * 默认连接必须配置,配置 key 为 default */
public static final String DEFAULT = "default";
private boolean enableMulti = false;
private Map<String, RedisProperties> multi;
public boolean isEnableMulti() {
return enableMulti;
}
public void setEnableMulti(boolean enableMulti) {
this.enableMulti = enableMulti;
}
public Map<String, RedisProperties> getMulti() {
return multi;
}
public void setMulti(Map<String, RedisProperties> multi) {
this.multi = multi;
}
public MultiRedisProperties() {}
}
代码测试:
配置文件配置多数据源:
spring.redis.enable-multi=true
spring.redis.multi.default.host=xxxxxxxx
spring.redis.multi.default.port=6381
spring.redis.multi.test.host=xxxxxxxx
spring.redis.multi.test.port=6380
配置redisTemplate
@Bean
public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
// 创建RedisTemplate<String, Object>对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂 template.setConnectionFactory(factory);
// 定义Jackson2JsonRedisSerializer序列化对象
Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
StringRedisSerializer stringSerial = new StringRedisSerializer();
// redis key 序列化方式使用stringSerial
template.setKeySerializer(stringSerial);
// redis value 序列化方式使用jackson
template.setValueSerializer(jacksonSeial);
// redis hash key 序列化方式使用stringSerial
template.setHashKeySerializer(stringSerial);
// redis hash value 序列化方式使用jackson
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}
编写测试代码:
package com.ducheng.multi.redis;
import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTestclass MultiRedisSourceApplicationTests {
@Autowired RedisTemplate<String, Object> redisTemplate;
@Autowired MultiRedisConnectionFactory multiRedisConnectionFactory;
@Test void contextLoads() {
// 走默认的数据源
redisTemplate.opsForValue().set("k1","v1");
// 走test数据源0 库
multiRedisConnectionFactory.setCurrentRedis("test",0);
// 走test数据源9 库
redisTemplate.opsForValue().set("k1","v2");
multiRedisConnectionFactory.setCurrentRedis("test",9);
redisTemplate.opsForValue().set("k1","v2"); }
}
最后结果
完美,自定义注解加上aop 来动态切换,就是定义一个自定义的注解里面包含库名称和db 的名称
然后 就是在aop 的前置拦截器上面,或者注解的值, 然后在用MultiRedisConnectionFactory 来设置数据源和db 。 相关的代码已发布到maven 私服, 使用教程请看 ducheng.github.io/dynamicRedi…
转载自:https://juejin.cn/post/7149472667995734024