spring.factories
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
RedisAutoConfiguration
package org.springframework.boot.autoconfigure.data.redis;
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class}) //自动配置properties 文件
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
LettuceConnectionConfiguration
package org.springframework.boot.autoconfigure.data.redis;
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisClient.class})
@ConditionalOnProperty(
name = {"spring.redis.client-type"},
havingValue = "lettuce",
matchIfMissing = true
)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
LettuceConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
}
//使用ObjectProvider<T>进行构造器注入,将ioc 中的对象注入到这里
@Bean(
destroyMethod = "shutdown"
)
@ConditionalOnMissingBean({ClientResources.class})
DefaultClientResources lettuceClientResources(ObjectProvider<ClientResourcesBuilderCustomizer> customizers) {
Builder builder = DefaultClientResources.builder();
customizers.orderedStream().forEach((customizer) -> {
customizer.customize(builder);
});
return builder.build();
}
// 创建RedisConnectionFactory并注入到容器
@Bean
@ConditionalOnMissingBean({RedisConnectionFactory.class})
LettuceConnectionFactory redisConnectionFactory(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources) {
// 获取lettuce客户端配置(获取配置逻辑在下面)
LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
return this.createLettuceConnectionFactory(clientConfig); //创建lettuce
}
//根据配置文件不同,创建不同的连接工厂,如果配置是哨兵则创建哨兵模式的连接工厂,如果是集群则创建集群的连接工厂,如果是单点则创建单点的连接工厂
private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
if (this.getSentinelConfig() != null) {
//创建哨兵模式的连接工厂
return new LettuceConnectionFactory(this.getSentinelConfig(), clientConfiguration);
} else {
return this.getClusterConfiguration() != null ? new LettuceConnectionFactory(this.getClusterConfiguration(), clientConfiguration) : new LettuceConnectionFactory(this.getStandaloneConfig(), clientConfiguration);
}
}
// 构建lettuce客户端连接配置
private LettuceClientConfiguration getLettuceClientConfiguration(ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers, ClientResources clientResources, Pool pool) {
// lettuce客户端配置建造者,通过建造者去解析url、连接主机、端口等信息,然后构建成一个lettuce客户端配置
LettuceClientConfigurationBuilder builder = this.createBuilder(pool);
this.applyProperties(builder);
if (StringUtils.hasText(this.getProperties().getUrl())) {
this.customizeConfigurationFromUrl(builder);
}
builder.clientOptions(this.createClientOptions());
builder.clientResources(clientResources);
builderCustomizers.orderedStream().forEach((customizer) -> {
customizer.customize(builder);
});
return builder.build();
}
private LettuceClientConfigurationBuilder createBuilder(Pool pool) {
return this.isPoolEnabled(pool) ? (new LettuceConnectionConfiguration.PoolBuilderFactory()).createBuilder(pool) : LettuceClientConfiguration.builder();
}
private LettuceClientConfigurationBuilder applyProperties(LettuceClientConfigurationBuilder builder) {
if (this.getProperties().isSsl()) {
builder.useSsl();
}
if (this.getProperties().getTimeout() != null) {
builder.commandTimeout(this.getProperties().getTimeout());
}
if (this.getProperties().getLettuce() != null) {
Lettuce lettuce = this.getProperties().getLettuce();
if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {
builder.shutdownTimeout(this.getProperties().getLettuce().getShutdownTimeout());
}
}
if (StringUtils.hasText(this.getProperties().getClientName())) {
builder.clientName(this.getProperties().getClientName());
}
return builder;
}
private ClientOptions createClientOptions() {
io.lettuce.core.ClientOptions.Builder builder = this.initializeClientOptionsBuilder();
Duration connectTimeout = this.getProperties().getConnectTimeout();
if (connectTimeout != null) {
builder.socketOptions(SocketOptions.builder().connectTimeout(connectTimeout).build());
}
return builder.timeoutOptions(TimeoutOptions.enabled()).build();
}
private io.lettuce.core.ClientOptions.Builder initializeClientOptionsBuilder() {
if (this.getProperties().getCluster() != null) {
io.lettuce.core.cluster.ClusterClientOptions.Builder builder = ClusterClientOptions.builder();
Refresh refreshProperties = this.getProperties().getLettuce().getCluster().getRefresh();
io.lettuce.core.cluster.ClusterTopologyRefreshOptions.Builder refreshBuilder = ClusterTopologyRefreshOptions.builder().dynamicRefreshSources(refreshProperties.isDynamicRefreshSources());
if (refreshProperties.getPeriod() != null) {
refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod());
}
if (refreshProperties.isAdaptive()) {
refreshBuilder.enableAllAdaptiveRefreshTriggers();
}
return builder.topologyRefreshOptions(refreshBuilder.build());
} else {
return ClientOptions.builder();
}
}
private void customizeConfigurationFromUrl(LettuceClientConfigurationBuilder builder) {
ConnectionInfo connectionInfo = this.parseUrl(this.getProperties().getUrl());
if (connectionInfo.isUseSsl()) {
builder.useSsl();
}
}
private static class PoolBuilderFactory {
private PoolBuilderFactory() {
}
LettuceClientConfigurationBuilder createBuilder(Pool properties) {
return LettucePoolingClientConfiguration.builder().poolConfig(this.getPoolConfig(properties));
}
private GenericObjectPoolConfig<?> getPoolConfig(Pool properties) {
GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig();
config.setMaxTotal(properties.getMaxActive());
config.setMaxIdle(properties.getMaxIdle());
config.setMinIdle(properties.getMinIdle());
if (properties.getTimeBetweenEvictionRuns() != null) {
config.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());
}
if (properties.getMaxWait() != null) {
config.setMaxWait(properties.getMaxWait());
}
return config;
}
}
}
RedisProperties
package org.springframework.boot.autoconfigure.data.redis;
@ConfigurationProperties(
prefix = "spring.redis"
) //加载前缀为spring.redis 的配置信息
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private RedisProperties.ClientType clientType;
private RedisProperties.Sentinel sentinel;
private RedisProperties.Cluster cluster;
private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();
public RedisProperties() {
}
public static class Lettuce {
private Duration shutdownTimeout = Duration.ofMillis(100L);
private final RedisProperties.Pool pool = new RedisProperties.Pool();
private final RedisProperties.Lettuce.Cluster cluster = new RedisProperties.Lettuce.Cluster();
public Lettuce() {
}
public Duration getShutdownTimeout() {
return this.shutdownTimeout;
}
public void setShutdownTimeout(Duration shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
public RedisProperties.Pool getPool() {
return this.pool;
}
public RedisProperties.Lettuce.Cluster getCluster() {
return this.cluster;
}
public static class Cluster {
private final RedisProperties.Lettuce.Cluster.Refresh refresh = new RedisProperties.Lettuce.Cluster.Refresh();
public Cluster() {
}
public RedisProperties.Lettuce.Cluster.Refresh getRefresh() {
return this.refresh;
}
public static class Refresh {
private boolean dynamicRefreshSources = true;
private Duration period;
private boolean adaptive;
public Refresh() {
}
public boolean isDynamicRefreshSources() {
return this.dynamicRefreshSources;
}
public void setDynamicRefreshSources(boolean dynamicRefreshSources) {
this.dynamicRefreshSources = dynamicRefreshSources;
}
public Duration getPeriod() {
return this.period;
}
public void setPeriod(Duration period) {
this.period = period;
}
public boolean isAdaptive() {
return this.adaptive;
}
public void setAdaptive(boolean adaptive) {
this.adaptive = adaptive;
}
}
}
}
public static class Jedis {
private final RedisProperties.Pool pool = new RedisProperties.Pool();
public Jedis() {
}
public RedisProperties.Pool getPool() {
return this.pool;
}
}
public static class Sentinel {
private String master;
private List<String> nodes;
private String password;
public Sentinel() {
}
public String getMaster() {
return this.master;
}
public void setMaster(String master) {
this.master = master;
}
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
public static class Cluster {
private List<String> nodes;
private Integer maxRedirects;
public Cluster() {
}
public List<String> getNodes() {
return this.nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
public Integer getMaxRedirects() {
return this.maxRedirects;
}
public void setMaxRedirects(Integer maxRedirects) {
this.maxRedirects = maxRedirects;
}
}
public static class Pool {
private Boolean enabled;
private int maxIdle = 8;
private int minIdle = 0;
private int maxActive = 8;
private Duration maxWait = Duration.ofMillis(-1L);
private Duration timeBetweenEvictionRuns;
public Pool() {
}
public Boolean getEnabled() {
return this.enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public int getMaxIdle() {
return this.maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMinIdle() {
return this.minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return this.maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public Duration getMaxWait() {
return this.maxWait;
}
public void setMaxWait(Duration maxWait) {
this.maxWait = maxWait;
}
public Duration getTimeBetweenEvictionRuns() {
return this.timeBetweenEvictionRuns;
}
public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) {
this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
}
}
public static enum ClientType {
LETTUCE,
JEDIS;
private ClientType() {
}
}
}
类图解析:
类名 | 类信息说明 |
RedisStandaloneConfiguration | redis单点配置 |
RedisSentinelConfiguration | redis 哨兵配置 |
RedisClusterConfiguration | redis集群配置 |
RedisProperties | redis属性文件文件配置类,从配置文件中读取属性文件,写入配置类中 |
RedisConnectionConfiguration | redis连接配置,是lettuce连接和jedis连接配置的抽象父类,封装了redis的各种配置方式 |
LettuceConnectionConfiguration | lettuce 客户端配置 |
JedisConnectionConfiguration | jedis客户端配置 |
RedisAutoConfiguration | springboot 中 redis的自动配置类 |
redis自动配置类加载时序图:
流程图解析:
②RedisConnectionConfiguration 构造方法调用
//为redis配置赋值
protected RedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider, ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider, ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
this.properties = properties;
this.standaloneConfiguration = (RedisStandaloneConfiguration)standaloneConfigurationProvider.getIfAvailable();
this.sentinelConfiguration = (RedisSentinelConfiguration)sentinelConfigurationProvider.getIfAvailable();
this.clusterConfiguration = (RedisClusterConfiguration)clusterConfigurationProvider.getIfAvailable();
}
解析,当RedisConnectionConfiguration的构造方法被子类LettuceConnectionConfiguration调用时,会为RedisProperties、RedisClusterConfiguration、RedisSentinelConfiguration、RedisStandaloneConfiguration这几个配置属性赋值。
⑤LettuceConnectionConfiguration 创建连接工厂
//根据配置文件不同,创建不同的连接工厂,如果配置是哨兵则创建哨兵模式的连接工厂,如果是集群则创建集群的连接工厂,如果是单点则创建单点的连接工厂
private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
if (this.getSentinelConfig() != null) {
//创建哨兵模式的连接工厂
return new LettuceConnectionFactory(this.getSentinelConfig(), clientConfiguration);
} else {
return this.getClusterConfiguration() != null ? new LettuceConnectionFactory(this.getClusterConfiguration(), clientConfiguration) : new LettuceConnectionFactory(this.getStandaloneConfig(), clientConfiguration);
}
}
解析:当createLettuceConnectionFactory()方法被调用时,会根据RedisConnectionConfiguration构造方法中对redis的配置初始化情况,选择配置对象不为空的配置对象创建连接工厂
项目连接集群配置示例:
#redis 配置
redis:
database: 1
lettuce:
pool:
max-active: 3000 #最大连接数据库连接数,设 -1 为没有限制
max-idle: 3000 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
shutdown-timeout: 100ms
password: xxxxxx
#standalone 使用此配置
#host: 10.1.x.xxx
#port: 6379
#cluster 使用此配置
cluster:
timeout: 5000
max-redirects: 3
nodes:
- 10.2.6.xx:6379
- 10.2.6.xx:6380
- 10.2.6.xx:6381
- 10.2.6.xx:6382
- 10.2.6.xx:6383
- 10.2.6.xx:6384