Springboot 中 Redis 自动配置源码分析

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值