一、博客背景
最近项目中有使用到redis做缓存队列,但是还提供了其他的缓存方式,所以redis需要做成可配置的,所以在这里记录下项目中的配置情况。
二、代码
将redis的所有相关文件放在一个application-redis.properties配置文件中,当需要使用该配置的时候在application.properties中的配置项中加上spring.profiles.active=redis
################### Redis配置#####################
#第一个redis集群的配置,用于基础数据的缓存
redis.useRedisModel=1
#集群模式连接
redis.cluster.nodes=192.169.1.26:7001,192.169.1.26:7002,192.169.1.26:7003,192.169.1.26:7004,192.169.1.26:7005,192.169.1.26:7006
#redis.cluster.nodes=192.169.1.196:7001,192.169.1.196:7002,192.169.1.196:7003,192.169.1.196:7004,192.169.1.196:7005,192.169.1.196:7006
#重试连接次数
redis.cluster.max-redirects=3
#最大连接数
redis.pool.max-active=50
#最大等待超时时间
redis.pool.max-wait=5
#最大空闲数
redis.pool.max-idle=50
#最小空闲数
redis.pool.min-idle=0
#超时时间
redis.timeout=50000
#使用的数据库
redis.database=0
#redis连接密码需要时放开
#redis.password=ENC(5M5nrpgKIQBkqEcldhbK6A==)
#redis.host=127.0.0.1
#redis.port=6379
条件控制类
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* redis相关bean是否注册条件类
*/
public class RedisCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//是否使用redis
String property = environment.getProperty("redis.useRedisModel");
return property != null && Integer.parseInt(property) == 0;
}
}
redis配置类
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.MapPropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
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.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RedisConfig {
/**最大活跃数**/
@Value("${redis.pool.max-active:8}")
private int maxActive;
/**最大等待数**/
@Value("${redis.pool.max-wait:-1}")
private int maxWait;
/**最大核心线程数**/
@Value("${redis.pool.max-idle:8}")
private int maxIdle;
/**最小核心线程数**/
@Value("${redis.pool.min-idle:0}")
private int minIdle;
/**redis连接的超时时长**/
@Value("${redis.timeout:100}")
private int timeOut;
/**redis连接的库**/
@Value("${redis.database:0}")
private int database;
/**节点配置**/
@Value("${redis.cluster.nodes:#{null}}")
private String nodes;
/**最大连接转移数**/
@Value("${redis.cluster.max-redirects:3}")
private int maxRedirects;
/**单节点情况下redis的ip**/
@Value("${redis.host:#{null}}")
private String host;
/**单节点情况下redis的端口**/
@Value("${redis.port:#{null}}")
private Integer port;
/**redis的连接密码**/
@Value("${redis.password:#{null}}")
private String password;
/**
* 创建redisTemplate
* 所有序列化k/v都会默认使用{@link JdkSerializationRedisSerializer}
* 默认的序列化类用使用byte数组进行存储, 在使用redis可视化客户端看到都是乱码不直观
* 使用StringRedisSerializer序列化Key, Jackson2JsonRedisSerializer序列化值, 可以观察到对象结构层次
*
* @return redisTemplate
*/
@Bean
@Qualifier("redisTemplate")
@Conditional(value = RedisCondition.class)
public <K, V> RedisTemplate<K, V> redisTemplate() {
Jackson2JsonRedisSerializer<Object> valueSerializer = jackson2JsonRedisSerializer();
StringRedisSerializer keySerializer = new StringRedisSerializer();
RedisTemplate<K, V> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashKeySerializer(keySerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
return redisTemplate;
}
/**
* 定义序列化类
*
* @return 序列化工具
*/
private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
return jackson2JsonRedisSerializer;
}
/**
* 连接配置
* @return redis连接工厂
*/
@Primary
@Bean
@Conditional(value = RedisCondition.class)
public RedisConnectionFactory connectionFactory() {
Map<String, Object> source = new HashMap<>(4);
RedisClusterConfiguration redisClusterConfiguration;
RedisStandaloneConfiguration redisStandaloneConfiguration;
//连接池配置
GenericObjectPoolConfig genericObjectPoolConfig =
new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxTotal(maxActive);
genericObjectPoolConfig.setMaxWaitMillis(maxWait);
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMinIdle(minIdle);
//redis客户端配置
LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
builder = LettucePoolingClientConfiguration.builder().
commandTimeout(Duration.ofSeconds(timeOut));
builder.poolConfig(genericObjectPoolConfig);
LettuceClientConfiguration lettuceClientConfiguration = builder.build();
//集群模式
if(StrUtil.isNotEmpty(nodes)){
source.put("spring.redis.cluster.nodes", nodes);
source.put("spring.redis.cluster.max-redirects", maxRedirects);
redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));
if(!StrUtil.isEmpty(password)){
redisClusterConfiguration.setPassword(password);
}
return new LettuceConnectionFactory(redisClusterConfiguration,lettuceClientConfiguration);
}else{
//单机模式
redisStandaloneConfiguration = new RedisStandaloneConfiguration(host,port);
redisStandaloneConfiguration.setDatabase(database);
if(!StrUtil.isEmpty(password)){
redisStandaloneConfiguration.setPassword(password);
}
return new LettuceConnectionFactory(redisStandaloneConfiguration,lettuceClientConfiguration);
}
}
}
三、代码讲解
在代码里我们定义了一个条件控制类RedisCondition,由这个类来控制是否注入redis的相关bean,换成具体的业务逻辑就是由
String property = environment.getProperty("redis.useRedisModel");
读取配置文件是否使用redis来控制
然后我们可以看到在redis的配置类里,我们可以看到无论是redis的连接工厂还是redistemplate的注解方法上都有加
@Conditional(value = RedisCondition.class)
这一行配置,即为如果RedisCondition.class的结果是真,则注入这两个bean
四、知识延伸
@Conditional注解
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
还有以下等注解
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnProperty
@ConditionalOnExpression
具体使用可以自行百度搜索