配置動態刷新RefreshScope注解使用局限性(一)


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

問題背景

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

@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;
}

系統報錯無法注入 RabbitTemplate

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#rabbitTemplate

@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 實現

    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

項目推薦: Spring Cloud 、Spring Security OAuth2的RBAC權限管理系統 歡迎關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM