【问题标题】:SpringBoot: how to inject two classes having same nameSpring Boot:如何注入两个具有相同名称的类
【发布时间】:2019-07-22 06:44:59
【问题描述】:

在我的应用程序中,我有两个具有相同名称的类,但当然位于不同的包中。

这两个类都需要在应用程序中注入;不幸的是,我收到以下错误消息:

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'myFeature' for bean class [org.pmesmeur.springboot.training.service.feature2.MyFeature] conflicts with existing, non-compatible bean definition of same name and class [org.pmesmeur.springboot.training.service.feature1.MyFeature]

我的问题可以通过以下示例重现:

@Component
@EnableConfigurationProperties(ServiceProperties.class)
public class MyService implements IService {

    private final ServiceProperties serviceProperties;
    private final IProvider provider;
    private final org.pmesmeur.springboot.training.service.feature1.IMyFeature f1;
    private final org.pmesmeur.springboot.training.service.feature2.IMyFeature f2;


    @Autowired
    public MyService(ServiceProperties serviceProperties,
                     IProvider provider,
                     org.pmesmeur.springboot.training.service.feature1.IMyFeature f1,
                     org.pmesmeur.springboot.training.service.feature2.IMyFeature f2) {
        this.serviceProperties = serviceProperties;
        this.provider = provider;
        this.f1 = f1;
        this.f2 = f2;
    }
    ...

package org.pmesmeur.springboot.training.service.feature1;

public interface IMyFeature {

    void print();

}

package org.pmesmeur.springboot.training.service.feature1;

import org.springframework.stereotype.Component;

@Component
public class MyFeature implements IMyFeature {

    @Override
    public void print() {
        System.out.print("HelloWorld");
    }

}

package org.pmesmeur.springboot.training.service.feature2;

public interface IMyFeature {

    void print();

}

package org.pmesmeur.springboot.training.service.feature2;

import org.springframework.stereotype.Component;


@Component
public class MyFeature implements IMyFeature {

    @Override
    public void print() {
        System.out.print("FooBar");
    }

}

如果我为我的课程使用不同的名称MyFeature,我的问题就消失了!!!

我习惯使用Guice,这个框架没有这种问题/限制

好像spring依赖注入框架只使用 类名而不是包名+类名,以便 选择它的类。

在“现实生活”中,我在一个更大的项目中遇到了这个问题,我强烈希望不必重命名我的类:有人可以帮助我吗?

最后一点,我宁愿避免使用“技巧”,例如使用 @Qualifier(value = "ABC") 注入我的课程时:在我的示例中, 找到正确的实例应该没有歧义 MyFeature 因为它们没有实现相同的接口

【问题讨论】:

  • 当 diff 包中的两个接口具有相同的抽象方法时,为什么不能使用单个接口?
  • 您是否在项目中的任何地方使用@Bean 注释?
  • 您能对我的回答提供一些反馈吗?我想帮助完成这个问题!!

标签: spring spring-boot dependency-injection guice


【解决方案1】:

简单地重新实现 BeanNameGenerator 为通过名称声明/实例化的 bean 添加了一个新问题

@Component("HelloWorld")
class MyComponent implements IComponent {
...
}

@Qualifier(value = "HelloWorld") IComponent component

我通过扩展AnnotationBeanNameGenerator和重新定义方法buildDefaultBeanName()解决了这个问题

static class BeanNameGeneratorIncludingPackageName extends AnnotationBeanNameGenerator {

    public BeanNameGeneratorIncludingPackageName() {
    }

    @Override
    public String buildDefaultBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
        return beanDefinition.getBeanClassName();
    }

}

【讨论】:

    【解决方案2】:

    您可以为每个组件分配一个值,例如@Component(value="someBean") 然后注入 @Qualifier 例如

    @Autowired
    public SomeService(@Qualifier("someBean") Some s){
       //....
    }
    

    【讨论】:

      【解决方案3】:

      你可以这样做;

      在包中a

      public class MyFeature implements IMyFeature {
      
          @Override
          public void print() {
              System.out.print("FooBar");
          }
      }
      

      在包中b

      public class MyFeature implements IMyFeature {
      
          @Override
          public void print() {
              System.out.print("HelloWorld");
          }
      }
      

      还有一些config class

      @Configuration
      public class Configuration {
      
          @Bean
          public a.MyFeature f1() {
              return new a.MyFeature();
          }
      
          @Bean
          public b.MyFeature f2() {
              return new b.MyFeature();
          }
      }
      

      然后您可以使用名称f1f2 自动装配它们,它们是它们各自的bean 构造方法的名称。

      你可以用@Component("f1") & 做类似的事情 @Component("f2")


      即使实现了不同的接口并且在不同的包中,相同的 bean 名称也会造成这种麻烦,您必须使用某种自定义命名来区分。与您使用上述解决方案所做的相比,使用一些 custom Spring logic 会太难看。

      【讨论】:

        【解决方案4】:

        Spring 按类型和名称提供自动装配。你的类名是一样的。默认情况下,spring 只考虑 className 而不是包。但是您可以通过定义 BeanNameGenerator 接口的自定义实现来覆盖此行为,在该接口中您可以使用包和名称生成名称。我不提供代码解决方案,因为我认为您应该对此进行更多探索。

        【讨论】:

          猜你喜欢
          • 2020-01-22
          • 2011-06-01
          • 2012-01-31
          • 1970-01-01
          • 2011-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多