首頁 > 軟體

springboot-啟動bean衝突的解決

2022-03-24 13:00:13

啟動bean衝突

在一次啟動中遇到了bean衝突的問題,提示存在兩個名稱重複的bean

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.test.api.Application]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'healthCheckController' for bean class [com.test.datahub.controller.HealthCheckController] conflicts with existing, non-compatible bean definition of same name and class [com.test.api.controller.HealthCheckController]

專案中包括多個模組,其中A、B兩個模組都有同一個類:

HealthCheckController,檢查更改資訊發現,不知道誰在A模組新增了B模組的依賴,造成了這一問題,刪除後解決

        <dependency>
            <groupId>com.test</groupId>
            <artifactId>B</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

啟動提示bean重複問題

先說結論

只需要在@FeignClient註解的contextId屬性上加上獨一的標示,即可解決問題

原理

是因為註冊feignClient的時候會註冊ClientConfiguration,參考程式碼如下

public void registerFeignClients(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   //...此處省略部分程式碼
   //
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidateComponents = scanner
            .findCandidateComponents(basePackage);
      for (BeanDefinition candidateComponent : candidateComponents) {
         if (candidateComponent instanceof AnnotatedBeanDefinition) {
            // verify annotated class is an interface
            //...省略部分程式碼
 
            //這塊是把註解上的所有屬性封裝到map上
            Map<String, Object> attributes = annotationMetadata
            .getAnnotationAttributes(
            FeignClient.class.getCanonicalName());
 
            //這兩個重點方法請看下面程式碼塊
 
            //獲取該feignClient的名字(重點關注該方法)
            String name = getClientName(attributes);
 
            //此方法就是spring注入beanDefination的步驟(重點關注該方法)
            registerClientConfiguration(registry, name,
                  attributes.get("configuration"));
 
            registerFeignClient(registry, annotationMetadata, attributes);
         }
      }
   }
}

上面的兩處重點方法, 請看此處

//@param client 這個map就是通過上面的註解屬性轉map得到的
private String getClientName(Map<String, Object> client) {
   if (client == null) {
      return null;
   }
   //如果從contextId獲取到名字,那麼value有值的
   String value = (String) client.get("contextId");
   //如果value有值,那麼如下3個if條件都不會走,所以contextId唯一就可以做到bean不重複,
   //如果value沒有值,就會取value,因為多個client的serverName都是一樣的,那麼就會出現重複bean
   if (!StringUtils.hasText(value)) {
      value = (String) client.get("value");
   }
   if (!StringUtils.hasText(value)) {
      value = (String) client.get("name");
   }
   if (!StringUtils.hasText(value)) {
      value = (String) client.get("serviceId");
   }
   if (StringUtils.hasText(value)) {
      return value;
   }
 
   throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
         + FeignClient.class.getSimpleName());
}
 
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
      Object configuration) {
   BeanDefinitionBuilder builder = BeanDefinitionBuilder
         .genericBeanDefinition(FeignClientSpecification.class);
   builder.addConstructorArgValue(name);
   builder.addConstructorArgValue(configuration);
   //在這個位置,建立beanDefinition,但是他建立的名字格式可以看出,唯一改變變數就是name,這個name就是上面看到的那個方法獲取的
   registry.registerBeanDefinition(
         name + "." + FeignClientSpecification.class.getSimpleName(),
         builder.getBeanDefinition());
}

以上就是feign導致的springBean重複問題的解釋,僅上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com