Я зарегистрировал проблему с SpringBoot, и она была принята...

Spring Boot задняя часть Примечания
Я зарегистрировал проблему с SpringBoot, и она была принята...

Вот в чем дело

В проекте используется Springboot + spring data redis, но компания оговаривает, что все пароли redis размещаются на хостинге и могут быть получены только удаленно.

Redis с одним экземпляром, используемый в среде разработки, и салат, используемый для пула соединений, реализуются путем копирования кода, автоматически загружаемого Spring Data Redis, в проект.Причину можно увидеть из следующего анализа, ядра конфигурации, связанной со Spring. Классы видны пакету и не могут быть унаследованы и на них нельзя ссылаться извне.

Однако добрые люди, то есть следующие, посчитали, что этого «весна не хватает», поэтому копнули немного глубже, и после некоторого анализа предложили сообществу более актуальный Issue, и он был принят.

Механизм автоподключения Spring Data Redis

существуетorg.springframework.boot.autoconfigure.data.redisимеютRedisAutoConfiguration, который проходит@Importзависит отLettuceConnectionConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class 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;
   }

}

LettuceConnectionConfigurationунаследовано отRedisConnectionConfiguration, основной код выглядит следующим образом

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisClient.class)  // -->①
@ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "lettuce", matchIfMissing = true)  // -->②
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {

	LettuceConnectionConfiguration(RedisProperties properties,
			ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
			ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
		super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
	}

	@Bean
	@ConditionalOnMissingBean(RedisConnectionFactory.class)  // -->③
	LettuceConnectionFactory redisConnectionFactory(
			ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
			ClientResources clientResources) {
		LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
				getProperties().getLettuce().getPool());
		return createLettuceConnectionFactory(clientConfig);
	}
}

Из этого видно, что условия загрузки Spring для автоматического подключения фабрики соединений Lettuce следующие:

① существуетRedisClient,lettuce.ioКлиентский класс Redis, который поставляется с

② Конфигурация, используемая в проектеspring.redis.client-typeзаlettuce

③ Пока это не определено в коде проектаRedisConnectionFactory, он будет создан автоматически в соответствии с конфигурационным файломLettuceConnectionFactory


Среди них есть два ключевых момента,

  • КонструкторLettuceConnectionConfigurationпоявившийсяRedisPropertiesи дваObjectProvider, а конструктор родительского класса вызывается
  • redisConnectionFactoryсодержит два важных методаgetLettuceClientConfigurationа такжеcreateLettuceConnectionFactory, вgetLettuceClientConfigurationВ основном он касается соответствующей конфигурации пула соединений пула, не вдаваясь в подробности. Вы также можете узнать из следующего анализа,propertiesНа самом деле этоRedisProperties, Сфокусируйся наcreateLettuceConnectionFactory

Ниже мы проанализируем эти ключевые моменты один за другим.

конструктор родительского классаRedisConnectionConfiguration

protected RedisConnectionConfiguration(RedisProperties properties,
      ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
      ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
   this.properties = properties;
   this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
   this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
}

Ключом к пониманию этого кода являетсяObjectProvider, На самом деле, если вы внимательно посмотрите, вы обнаружите, что код Springboot, особенно конструктор, использует многоObjectProvider

ObjectProvider

оObjectProvider, можно вкратце рассказать о некоторых улучшениях Spring 4.3

Когда параметр конструктора является одним параметром конструктора, вы можете аннотировать с помощью @Autowired

@Service
public class FooService {
    private final FooRepository repository;
    public FooService(FooRepository repository) {
        this.repository = repository
    }
}

Например, приведенный выше код является версией после весны 4.3, не нужно@Autowiredтоже нормально работает.

Также в версии Spring 4.3 неявно внедряются не только свойства параметров одиночной конструкции, но и введениеObjectProviderинтерфейс.

//A variant of ObjectFactory designed specifically for injection points, allowing for programmatic optionality and lenient not-unique handling.
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
    // ...省略了部分代码
    @Nullable
	T getIfAvailable() throws BeansException;
}

Как видно из комментариев к исходному коду,ObjectProviderинтерфейсObjectFactoryРасширения интерфейсов, разработанные специально для точек внедрения, могут сделать внедрение более разрешительным и необязательным.

Среди них, поgetIfAvailable()Можно видеть, что когда Bean-компонент для ввода параметров пуст или имеет несколько параметров, онObjectProviderкогда это работает.

  • Если внедренный экземпляр пуст, используйтеObjectProviderЭто позволяет избежать исключения, когда зависимый объект не существует из-за сильных зависимостей.
  • Если экземпляров несколько,ObjectProviderМетод будет реализован в соответствии с BeanOrderedинтерфейс или@OrderПолучить один в порядке, указанном в аннотацииBean, тем самым обеспечивая более расслабленный способ внедрения зависимостей

назад,RedisConnectionConfigurationЭтот конструктор родительского класса сам фактически реализует такую ​​функцию: если пользователь предоставляетRedisSentinelConfigurationа такжеRedisSentinelConfiguration, будет загружен в конструктор, иRedisPropertiesЭто относительно просто, то есть связанная конфигурация Redis.

RedisProperties

Прочтите соответствующую конфигурацию redis из конфигурации.Простейшая конфигурация redis для одной машины — это простые атрибуты, sentinel — это связанная конфигурация дозорного, кластер — это связанная конфигурация кластера, а пул — это связанная конфигурация пула соединений. .

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
	private int database = 0;
	private String url;
	private String host = "localhost";
	private int port = 6379;

	private String username;
	private String password;

	private Sentinel sentinel;
	private Cluster cluster;
	public static class Pool {}
	public static class Cluster {}
	public static class Sentinel {}
	// ... 省略非必要代码
}

Подводя итог, в настоящее время мы можем видетьRedisAutoConfigurationЗависит от класса конфигурацииLettuceConnectionConfiguration, его конструктор считывает определяемую пользователем конфигурацию redis, которая включает в себя конфигурацию для одного компьютера + конфигурацию кластера + конфигурацию дозорного + конфигурацию пула соединений, из которых конфигурация кластера и конфигурация дозорного являются двумя bean-компонентами, которые позволяют пользователям настраивать.

createLettuceConnectionFactory

LettuceConnectionConfigurationВызывается в методе, реализующем пул соединений вcreateLettuceConnectionFactory, который реализуется следующим образом

private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
		if (getSentinelConfig() != null) {
			return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
		}
		if (getClusterConfiguration() != null) {
			return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
		}
		return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
	}

По сути, это прочитать конфигурацию дозорного, конфигурацию кластера и конфигурацию одиночной машины по очереди, и если она есть, создать пул подключений и вернуть его.

вgetSentinelConfig()а такжеgetClusterConfiguration()это метод родительского класса, который реализован следующим образом,

protected final RedisSentinelConfiguration getSentinelConfig() {
   if (this.sentinelConfiguration != null) {
      return this.sentinelConfiguration;
   }
   RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel();
   if (sentinelProperties != null) {
      RedisSentinelConfiguration config = new RedisSentinelConfiguration();
      // 省略装载代码
      config.setDatabase(this.properties.getDatabase());
      return config;
   }
   return null;
}

protected final RedisClusterConfiguration getClusterConfiguration() {
   if (this.clusterConfiguration != null) {
      return this.clusterConfiguration;
   }
   if (this.properties.getCluster() == null) {
      return null;
   }
   RedisProperties.Cluster clusterProperties = this.properties.getCluster();
   RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
   // 省略装载代码
   return config;
}

Отсюда мы можем узнать, что его приоритет читается в конструкторе наObjectProviderВведены определяемые пользователем компоненты конфигурации, которые могут существовать, если нет, прочтитеRedisPropertiesЗавершите сборку.

Однако внимательные читатели должны спросить: а как насчет конфигурации с одной машиной?

image-20210917000348170.png

protected final RedisStandaloneConfiguration getStandaloneConfig() {
   RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
   if (StringUtils.hasText(this.properties.getUrl())) {
      // 省略装载代码
   }
   else {
      // 省略装载代码
   }
   config.setDatabase(this.properties.getDatabase());
   return config;
}

Да, вы правильно прочитали, одинокие собаки не заслуживают...

src=http___dn-p-tystore.qbox.me_p_chapter_attachment_eBjVEBIWES_Egfveg6VeB6segjUetbteluCfn9rG7Ldgn5Gig5aJHeOHuHa90ueJ6S.jpg&refer=http___dn-p-tystore.qbox.jpg

Подводя итог, получите соответствующий bean-компонент конфигурации в конструкторе, а затем найдите его в методе создания пула соединений.Если нет, используйте файл конфигурации для его создания, но redis с одним экземпляром не поддерживается.

поднять вопрос

Защита одиночных собак является обязанностью каждого, поэтому яОт имени «Ассоциации защиты одиноких собак»Подняли вопрос в сообществе SpringBoot

image-20210917000825318.pngТогда начальник ответил:может защититьРад поддержать.

image-20210917000939528.pngСреди них отмечается, что использованиеBeanPostProcessorметод перезаписиRedisPropertiesЯ думал о конфигурации .