• Springboot 中 Redis 自动配置源码分析


    spring.factories

    org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\

    RedisAutoConfiguration

    1. package org.springframework.boot.autoconfigure.data.redis;
    2. @Configuration(
    3. proxyBeanMethods = false
    4. )
    5. @ConditionalOnClass({RedisOperations.class})
    6. @EnableConfigurationProperties({RedisProperties.class}) //自动配置properties 文件
    7. @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
    8. public class RedisAutoConfiguration {
    9. public RedisAutoConfiguration() {
    10. }
    11. @Bean
    12. @ConditionalOnMissingBean(
    13. name = {"redisTemplate"}
    14. )
    15. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    16. public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    17. RedisTemplate template = new RedisTemplate();
    18. template.setConnectionFactory(redisConnectionFactory);
    19. return template;
    20. }
    21. @Bean
    22. @ConditionalOnMissingBean
    23. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    24. public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    25. return new StringRedisTemplate(redisConnectionFactory);
    26. }
    27. }

     LettuceConnectionConfiguration

    1. package org.springframework.boot.autoconfigure.data.redis;
    2. @Configuration(
    3. proxyBeanMethods = false
    4. )
    5. @ConditionalOnClass({RedisClient.class})
    6. @ConditionalOnProperty(
    7. name = {"spring.redis.client-type"},
    8. havingValue = "lettuce",
    9. matchIfMissing = true
    10. )
    11. class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
    12. LettuceConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfigurationProvider, ObjectProvider clusterConfigurationProvider) {
    13. super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
    14. }
    15. //使用ObjectProvider进行构造器注入,将ioc 中的对象注入到这里
    16. @Bean(
    17. destroyMethod = "shutdown"
    18. )
    19. @ConditionalOnMissingBean({ClientResources.class})
    20. DefaultClientResources lettuceClientResources(ObjectProvider customizers) {
    21. Builder builder = DefaultClientResources.builder();
    22. customizers.orderedStream().forEach((customizer) -> {
    23. customizer.customize(builder);
    24. });
    25. return builder.build();
    26. }
    27. // 创建RedisConnectionFactory并注入到容器
    28. @Bean
    29. @ConditionalOnMissingBean({RedisConnectionFactory.class})
    30. LettuceConnectionFactory redisConnectionFactory(ObjectProvider builderCustomizers, ClientResources clientResources) {
    31. // 获取lettuce客户端配置(获取配置逻辑在下面)
    32. LettuceClientConfiguration clientConfig = this.getLettuceClientConfiguration(builderCustomizers, clientResources, this.getProperties().getLettuce().getPool());
    33. return this.createLettuceConnectionFactory(clientConfig); //创建lettuce
    34. }
    35. //根据配置文件不同,创建不同的连接工厂,如果配置是哨兵则创建哨兵模式的连接工厂,如果是集群则创建集群的连接工厂,如果是单点则创建单点的连接工厂
    36. private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
    37. if (this.getSentinelConfig() != null) {
    38. //创建哨兵模式的连接工厂
    39. return new LettuceConnectionFactory(this.getSentinelConfig(), clientConfiguration);
    40. } else {
    41. return this.getClusterConfiguration() != null ? new LettuceConnectionFactory(this.getClusterConfiguration(), clientConfiguration) : new LettuceConnectionFactory(this.getStandaloneConfig(), clientConfiguration);
    42. }
    43. }
    44. // 构建lettuce客户端连接配置
    45. private LettuceClientConfiguration getLettuceClientConfiguration(ObjectProvider builderCustomizers, ClientResources clientResources, Pool pool) {
    46. // lettuce客户端配置建造者,通过建造者去解析url、连接主机、端口等信息,然后构建成一个lettuce客户端配置
    47. LettuceClientConfigurationBuilder builder = this.createBuilder(pool);
    48. this.applyProperties(builder);
    49. if (StringUtils.hasText(this.getProperties().getUrl())) {
    50. this.customizeConfigurationFromUrl(builder);
    51. }
    52. builder.clientOptions(this.createClientOptions());
    53. builder.clientResources(clientResources);
    54. builderCustomizers.orderedStream().forEach((customizer) -> {
    55. customizer.customize(builder);
    56. });
    57. return builder.build();
    58. }
    59. private LettuceClientConfigurationBuilder createBuilder(Pool pool) {
    60. return this.isPoolEnabled(pool) ? (new LettuceConnectionConfiguration.PoolBuilderFactory()).createBuilder(pool) : LettuceClientConfiguration.builder();
    61. }
    62. private LettuceClientConfigurationBuilder applyProperties(LettuceClientConfigurationBuilder builder) {
    63. if (this.getProperties().isSsl()) {
    64. builder.useSsl();
    65. }
    66. if (this.getProperties().getTimeout() != null) {
    67. builder.commandTimeout(this.getProperties().getTimeout());
    68. }
    69. if (this.getProperties().getLettuce() != null) {
    70. Lettuce lettuce = this.getProperties().getLettuce();
    71. if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {
    72. builder.shutdownTimeout(this.getProperties().getLettuce().getShutdownTimeout());
    73. }
    74. }
    75. if (StringUtils.hasText(this.getProperties().getClientName())) {
    76. builder.clientName(this.getProperties().getClientName());
    77. }
    78. return builder;
    79. }
    80. private ClientOptions createClientOptions() {
    81. io.lettuce.core.ClientOptions.Builder builder = this.initializeClientOptionsBuilder();
    82. Duration connectTimeout = this.getProperties().getConnectTimeout();
    83. if (connectTimeout != null) {
    84. builder.socketOptions(SocketOptions.builder().connectTimeout(connectTimeout).build());
    85. }
    86. return builder.timeoutOptions(TimeoutOptions.enabled()).build();
    87. }
    88. private io.lettuce.core.ClientOptions.Builder initializeClientOptionsBuilder() {
    89. if (this.getProperties().getCluster() != null) {
    90. io.lettuce.core.cluster.ClusterClientOptions.Builder builder = ClusterClientOptions.builder();
    91. Refresh refreshProperties = this.getProperties().getLettuce().getCluster().getRefresh();
    92. io.lettuce.core.cluster.ClusterTopologyRefreshOptions.Builder refreshBuilder = ClusterTopologyRefreshOptions.builder().dynamicRefreshSources(refreshProperties.isDynamicRefreshSources());
    93. if (refreshProperties.getPeriod() != null) {
    94. refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod());
    95. }
    96. if (refreshProperties.isAdaptive()) {
    97. refreshBuilder.enableAllAdaptiveRefreshTriggers();
    98. }
    99. return builder.topologyRefreshOptions(refreshBuilder.build());
    100. } else {
    101. return ClientOptions.builder();
    102. }
    103. }
    104. private void customizeConfigurationFromUrl(LettuceClientConfigurationBuilder builder) {
    105. ConnectionInfo connectionInfo = this.parseUrl(this.getProperties().getUrl());
    106. if (connectionInfo.isUseSsl()) {
    107. builder.useSsl();
    108. }
    109. }
    110. private static class PoolBuilderFactory {
    111. private PoolBuilderFactory() {
    112. }
    113. LettuceClientConfigurationBuilder createBuilder(Pool properties) {
    114. return LettucePoolingClientConfiguration.builder().poolConfig(this.getPoolConfig(properties));
    115. }
    116. private GenericObjectPoolConfig getPoolConfig(Pool properties) {
    117. GenericObjectPoolConfig config = new GenericObjectPoolConfig();
    118. config.setMaxTotal(properties.getMaxActive());
    119. config.setMaxIdle(properties.getMaxIdle());
    120. config.setMinIdle(properties.getMinIdle());
    121. if (properties.getTimeBetweenEvictionRuns() != null) {
    122. config.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());
    123. }
    124. if (properties.getMaxWait() != null) {
    125. config.setMaxWait(properties.getMaxWait());
    126. }
    127. return config;
    128. }
    129. }
    130. }

    RedisProperties

    1. package org.springframework.boot.autoconfigure.data.redis;
    2. @ConfigurationProperties(
    3. prefix = "spring.redis"
    4. ) //加载前缀为spring.redis 的配置信息
    5. public class RedisProperties {
    6. private int database = 0;
    7. private String url;
    8. private String host = "localhost";
    9. private String username;
    10. private String password;
    11. private int port = 6379;
    12. private boolean ssl;
    13. private Duration timeout;
    14. private Duration connectTimeout;
    15. private String clientName;
    16. private RedisProperties.ClientType clientType;
    17. private RedisProperties.Sentinel sentinel;
    18. private RedisProperties.Cluster cluster;
    19. private final RedisProperties.Jedis jedis = new RedisProperties.Jedis();
    20. private final RedisProperties.Lettuce lettuce = new RedisProperties.Lettuce();
    21. public RedisProperties() {
    22. }
    23. public static class Lettuce {
    24. private Duration shutdownTimeout = Duration.ofMillis(100L);
    25. private final RedisProperties.Pool pool = new RedisProperties.Pool();
    26. private final RedisProperties.Lettuce.Cluster cluster = new RedisProperties.Lettuce.Cluster();
    27. public Lettuce() {
    28. }
    29. public Duration getShutdownTimeout() {
    30. return this.shutdownTimeout;
    31. }
    32. public void setShutdownTimeout(Duration shutdownTimeout) {
    33. this.shutdownTimeout = shutdownTimeout;
    34. }
    35. public RedisProperties.Pool getPool() {
    36. return this.pool;
    37. }
    38. public RedisProperties.Lettuce.Cluster getCluster() {
    39. return this.cluster;
    40. }
    41. public static class Cluster {
    42. private final RedisProperties.Lettuce.Cluster.Refresh refresh = new RedisProperties.Lettuce.Cluster.Refresh();
    43. public Cluster() {
    44. }
    45. public RedisProperties.Lettuce.Cluster.Refresh getRefresh() {
    46. return this.refresh;
    47. }
    48. public static class Refresh {
    49. private boolean dynamicRefreshSources = true;
    50. private Duration period;
    51. private boolean adaptive;
    52. public Refresh() {
    53. }
    54. public boolean isDynamicRefreshSources() {
    55. return this.dynamicRefreshSources;
    56. }
    57. public void setDynamicRefreshSources(boolean dynamicRefreshSources) {
    58. this.dynamicRefreshSources = dynamicRefreshSources;
    59. }
    60. public Duration getPeriod() {
    61. return this.period;
    62. }
    63. public void setPeriod(Duration period) {
    64. this.period = period;
    65. }
    66. public boolean isAdaptive() {
    67. return this.adaptive;
    68. }
    69. public void setAdaptive(boolean adaptive) {
    70. this.adaptive = adaptive;
    71. }
    72. }
    73. }
    74. }
    75. public static class Jedis {
    76. private final RedisProperties.Pool pool = new RedisProperties.Pool();
    77. public Jedis() {
    78. }
    79. public RedisProperties.Pool getPool() {
    80. return this.pool;
    81. }
    82. }
    83. public static class Sentinel {
    84. private String master;
    85. private List nodes;
    86. private String password;
    87. public Sentinel() {
    88. }
    89. public String getMaster() {
    90. return this.master;
    91. }
    92. public void setMaster(String master) {
    93. this.master = master;
    94. }
    95. public List getNodes() {
    96. return this.nodes;
    97. }
    98. public void setNodes(List nodes) {
    99. this.nodes = nodes;
    100. }
    101. public String getPassword() {
    102. return this.password;
    103. }
    104. public void setPassword(String password) {
    105. this.password = password;
    106. }
    107. }
    108. public static class Cluster {
    109. private List nodes;
    110. private Integer maxRedirects;
    111. public Cluster() {
    112. }
    113. public List getNodes() {
    114. return this.nodes;
    115. }
    116. public void setNodes(List nodes) {
    117. this.nodes = nodes;
    118. }
    119. public Integer getMaxRedirects() {
    120. return this.maxRedirects;
    121. }
    122. public void setMaxRedirects(Integer maxRedirects) {
    123. this.maxRedirects = maxRedirects;
    124. }
    125. }
    126. public static class Pool {
    127. private Boolean enabled;
    128. private int maxIdle = 8;
    129. private int minIdle = 0;
    130. private int maxActive = 8;
    131. private Duration maxWait = Duration.ofMillis(-1L);
    132. private Duration timeBetweenEvictionRuns;
    133. public Pool() {
    134. }
    135. public Boolean getEnabled() {
    136. return this.enabled;
    137. }
    138. public void setEnabled(Boolean enabled) {
    139. this.enabled = enabled;
    140. }
    141. public int getMaxIdle() {
    142. return this.maxIdle;
    143. }
    144. public void setMaxIdle(int maxIdle) {
    145. this.maxIdle = maxIdle;
    146. }
    147. public int getMinIdle() {
    148. return this.minIdle;
    149. }
    150. public void setMinIdle(int minIdle) {
    151. this.minIdle = minIdle;
    152. }
    153. public int getMaxActive() {
    154. return this.maxActive;
    155. }
    156. public void setMaxActive(int maxActive) {
    157. this.maxActive = maxActive;
    158. }
    159. public Duration getMaxWait() {
    160. return this.maxWait;
    161. }
    162. public void setMaxWait(Duration maxWait) {
    163. this.maxWait = maxWait;
    164. }
    165. public Duration getTimeBetweenEvictionRuns() {
    166. return this.timeBetweenEvictionRuns;
    167. }
    168. public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) {
    169. this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
    170. }
    171. }
    172. public static enum ClientType {
    173. LETTUCE,
    174. JEDIS;
    175. private ClientType() {
    176. }
    177. }
    178. }

    类图解析:

    类名

    类信息说明

    RedisStandaloneConfiguration

    redis单点配置

    RedisSentinelConfiguration

    redis 哨兵配置

    RedisClusterConfiguration

    redis集群配置

    RedisProperties

    redis属性文件文件配置类,从配置文件中读取属性文件,写入配置类中

    RedisConnectionConfiguration

    redis连接配置,是lettuce连接和jedis连接配置的抽象父类,封装了redis的各种配置方式

    LettuceConnectionConfiguration

    lettuce 客户端配置

    JedisConnectionConfiguration

    jedis客户端配置

    RedisAutoConfiguration

    springboot 中 redis的自动配置类 

     redis自动配置类加载时序图:

     

    流程图解析:

    ②RedisConnectionConfiguration 构造方法调用

    1. //为redis配置赋值
    2. protected RedisConnectionConfiguration(RedisProperties properties, ObjectProvider standaloneConfigurationProvider, ObjectProvider sentinelConfigurationProvider, ObjectProvider clusterConfigurationProvider) {
    3. this.properties = properties;
    4. this.standaloneConfiguration = (RedisStandaloneConfiguration)standaloneConfigurationProvider.getIfAvailable();
    5. this.sentinelConfiguration = (RedisSentinelConfiguration)sentinelConfigurationProvider.getIfAvailable();
    6. this.clusterConfiguration = (RedisClusterConfiguration)clusterConfigurationProvider.getIfAvailable();
    7. }

    解析,当RedisConnectionConfiguration的构造方法被子类LettuceConnectionConfiguration调用时,会为RedisProperties、RedisClusterConfiguration、RedisSentinelConfiguration、RedisStandaloneConfiguration这几个配置属性赋值。

    ⑤LettuceConnectionConfiguration 创建连接工厂

    1. //根据配置文件不同,创建不同的连接工厂,如果配置是哨兵则创建哨兵模式的连接工厂,如果是集群则创建集群的连接工厂,如果是单点则创建单点的连接工厂
    2. private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
    3. if (this.getSentinelConfig() != null) {
    4. //创建哨兵模式的连接工厂
    5. return new LettuceConnectionFactory(this.getSentinelConfig(), clientConfiguration);
    6. } else {
    7. return this.getClusterConfiguration() != null ? new LettuceConnectionFactory(this.getClusterConfiguration(), clientConfiguration) : new LettuceConnectionFactory(this.getStandaloneConfig(), clientConfiguration);
    8. }
    9. }

     解析:当createLettuceConnectionFactory()方法被调用时,会根据RedisConnectionConfiguration构造方法中对redis的配置初始化情况,选择配置对象不为空的配置对象创建连接工厂

    项目连接集群配置示例:

    1. #redis 配置
    2. redis:
    3. database: 1
    4. lettuce:
    5. pool:
    6. max-active: 3000 #最大连接数据库连接数,设 -1 为没有限制
    7. max-idle: 3000 #最大等待连接中的数量,设 0 为没有限制
    8. max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
    9. min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
    10. shutdown-timeout: 100ms
    11. password: xxxxxx
    12. #standalone 使用此配置
    13. #host: 10.1.x.xxx
    14. #port: 6379
    15. #cluster 使用此配置
    16. cluster:
    17. timeout: 5000
    18. max-redirects: 3
    19. nodes:
    20. - 10.2.6.xx:6379
    21. - 10.2.6.xx:6380
    22. - 10.2.6.xx:6381
    23. - 10.2.6.xx:6382
    24. - 10.2.6.xx:6383
    25. - 10.2.6.xx:6384

  • 相关阅读:
    力扣(LeetCode)1333. 餐厅过滤器(C++)
    JavaScript - 手写call、apply和bind函数
    Linux进程控制
    软件安全性测试包含哪些类型?2023年专业软件安全测试报告获取
    Golang Context 的使用指南
    [PostgreSql]生产级别数据库安装要考虑哪些问题?
    【通信】基于增广矩阵束的L型阵列的二维DOA估计附matlab完整代码
    基于Spring AOP和CGLIB代理实现引介增强(Introduction Advice)示例
    代码随想录训练营二刷第二十三天 | 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树
    docker部署frp穿透内网
  • 原文地址:https://blog.csdn.net/donkeyboy001/article/details/128018679