配置動態刷新RefreshScope註解使用侷限性(一)

在 Spring Cloud 體系的項目中,配置中心主要用於提供分佈式的配置管理,其中有一個重要的註解:@RefreshScope,若是代碼中須要動態刷新配置,在須要的類上加上該註解就行。本文分享一下筆者遇到與 @ConditionalOnSingleCandidate 註解衝突的問題java

問題背景

項目再引入 RabbitMQ,在自定義 connectionFactory 時,手滑加上了 @RefreshScopegit

@Bean
@RefreshScope
public CachingConnectionFactory connectionFactory() {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setAddresses("172.17.0.111");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");
    return connectionFactory;
}複製代碼

系統報錯沒法注入 RabbitTemplategithub

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.pig4cloud.course.refresh.bug.RefreshBugApplicationTest':

Unsatisfied dependency expressed through field 'rabbitTemplate'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:

No qualifying bean of type 'org.springframework.amqp.rabbit.core.RabbitTemplate' available: expected at least 1 bean which qualifies as autowire candidate.

Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
複製代碼

排查

    1. 默認狀況下 spring-boot-starter-amqp 會默認給咱們注入 rabbitTemplate 實現

RabbitAutoConfiguration#rabbitTemplatespring

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(RabbitOperations.class)
public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
  RabbitTemplate template = new RabbitTemplate();
  configurer.configure(template, connectionFactory);
  return template;
}複製代碼
    1. 開啓 Spring Boot 啓動 debugger 日誌,查看注入信息
RabbitAutoConfiguration.RabbitTemplateConfiguration#rabbitTemplate:
      Did not match:
         - @ConditionalOnSingleCandidate (types: org.springframework.amqp.rabbit.connection.ConnectionFactory; SearchStrategy: all)

         did not find a primary bean from beans 'connectionFactory', 'scopedTarget.connectionFactory' (OnBeanCondition)複製代碼

提示 ConditionalOnSingleCandidate 註解的方法不能查找到惟一 ConnectionFactory 實現express

    1. 使用 @RefreshScope 註解的 bean,默認狀況下同時會生成 scopedTarget.beanName的 bean
@Autowired
private ApplicationContext context;

@Test
public void testRabbitTemplate() {
    String[] beanNames = context.getBeanNamesForType(ConnectionFactory.class);

    for (String beanName : beanNames) {
        System.out.println(beanName);
    }

    Assert.isTrue(beanNames.length == 2);
}

scopedTarget.connectionFactory
connectionFactory複製代碼
    1. 因爲 ConditionalOnSingleCandidate 成立的條件是全局只能有一個此類型的 bean 因此 默認的 RabbitTemplate 沒法注入

常見被 ConditionalOnSingleCandidate 註解的 bean

    1. 使用 JdbcTemplate 沒法在自定義 DataSource 添加 @RefreshScope
@ConditionalOnSingleCandidate(DataSource.class)
public class JdbcTemplateAutoConfiguration {}複製代碼
    1. MailSenderValidator 郵件發送校驗器沒法添加 @RefreshScope
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(MailSenderAutoConfiguration.class)
@ConditionalOnProperty(prefix = "spring.mail", value = "test-connection")
@ConditionalOnSingleCandidate(JavaMailSenderImpl.class)
public class MailSenderValidatorAutoConfiguration {}複製代碼
    1. 因爲默認狀況下涉及的 bean 不少,因此使用 RefreshScope 時必定要避免這些 bean

image

相關文章
相關標籤/搜索