Java Spring无法自动装配,有不止一个``类型的bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37565186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:27:33  来源:igfitidea点击:

Spring Couldn't autowired,there is more than one bean of `` type

javaspring

提问by goudaner

Here is my question:I have a base interface and two implementation class.

这是我的问题:我有一个基本接口和两个实现类。

And a Service class has a dependencies on the base interface, the code is like this:

并且一个Service类对基接口有依赖,代码是这样的:

@Component
public interface BaseInterface {}


@Component
public class ClazzImplA implements  BaseInterface{}


@Component
public class ClazzImplB implements  BaseInterface{}


And the configuration is like this :

配置是这样的:

@Configuration
public class SpringConfig {
    @Bean
    public BaseInterface clazzImplA(){
        return new ClazzImplA();
    }

    @Bean
    public BaseInterface clazzImplB(){
        return new ClazzImplB();
    }
}


The service class has dependencies on the base interface will decide to autowire which Implementation by some business logic.And the code is like this:

服务类依赖于基接口,将决定通过某些业务逻辑自动装配哪个实现。代码如下:



@Service
@SpringApplicationConfiguration(SpringConfig.class)
public class AutowiredClazz {
    @Autowired
    private BaseInterface baseInterface;

    private AutowiredClazz(BaseInterface baseInterface){
        this.baseInterface = baseInterface;
    }
}

And the IDEA throws a exception:Could not autowire.There is more than one bean of BaseInterfacetype.

并且 IDEA 抛出异常:无法自动装配。有多个BaseInterface类型的bean 。

Although it can be solved by using @Qualifier,but in this situation I can't choose the dependencies class.

虽然可以通过使用@Qualifier 解决,但是在这种情况下我无法选择依赖项类。

@Autowired
@Qualifier("clazzImplA")
private BaseInterface baseInterface;

I tried to read the spring document and it provide a Constructor-based dependency injectionbut I'm still confused by the problem.

我试图阅读 spring 文档,它提供了一个Constructor-based dependency injection但我仍然对这个问题感到困惑。

can anyone help me ?

谁能帮我 ?

采纳答案by Mohamed Nabli

Spring is confused between the 2 beans you have declared in you configuration class so you can use @Qualifierannotation along with @Autowiredto remove the confusion by specifying which exact bean will be wired, apply these modifications on your configuration class

Spring 在您在配置类中声明的 2 个 bean 之间混淆,因此您可以使用@Qualifier注释和@Autowired通过指定将连接的确切 bean 来消除混淆,将这些修改应用于您的配置类

@Configuration
public class SpringConfig {
    @Bean(name="clazzImplA")
    public BaseInterface clazzImplA(){
        return new ClazzImplA();
    }

    @Bean(name="clazzImplB")
    public BaseInterface clazzImplB(){
        return new ClazzImplB();
    }
}

then at @autowiredannotation

然后在@autowired注释处

@Service
@SpringApplicationConfiguration(SpringConfig.class)
public class AutowiredClazz {
    @Autowired
    @Qualifier("the name of the desired bean")
    private BaseInterface baseInterface;

    private AutowiredClazz(BaseInterface baseInterface){
        this.baseInterface = baseInterface;
    }
}

回答by André Stannek

If you use @Autowired, Spring searches for a bean matching the type of the field you want to autowire. In your case there is more than one bean of the type BaseInterface. That means that Spring can't pick a matching bean unambiguously.

如果您使用@Autowired,Spring 会搜索与您要自动装配的字段类型匹配的 bean。在您的情况下,有多个类型的 bean BaseInterface。这意味着 Spring 不能明确地选择匹配的 bean。

In such a situation you have no other choice to explicitly state the bean Spring should use or resolve the ambiguity.

在这种情况下,您没有其他选择来明确声明 Spring 应该使用的 bean 或解决歧义。

回答by Sangram Jadhav

This can not be solved by using spring framework only. You mentioned that based on some logic you need a instance of BaseInterface. This use case can be solved using Factory Pattern. Create A Bean which is actually a factory for BaseInterface

仅使用 spring 框架无法解决此问题。您提到基于某些逻辑,您需要一个 BaseInterface 实例。这个用例可以使用工厂模式解决。创建一个 Bean,它实际上是 BaseInterface 的工厂

@Component
public class BaseInterfaceFactory{

  @Autowired
  @Qualifier("clazzImplA")
  private BaseInterface baseInterfaceA;

  @Autowired
  @Qualifier("clazzImplb")
  private BaseInterface baseInterfaceB;

  public BaseInterface getInstance(parameters which will decides what type of instance you want){
    // your logic to choose Instance A or Instance B
    return baseInterfaceA or baseInterfaceB
  }

}

Configuration (Shamelessly copied from another comment)

配置(无耻地从另一个评论复制)

@Configuration
public class SpringConfig {
    @Bean(name="clazzImplA")
    public BaseInterface clazzImplA(){
        return new ClazzImplA();
    }

    @Bean(name="clazzImplB")
    public BaseInterface clazzImplB(){
        return new ClazzImplB();
    }
}

Service class

服务类

@Service
@SpringApplicationConfiguration(SpringConfig.class)
public class AutowiredClazz {
    @Autowired
    private BaseInterfaceFactory factory;

    public void someMethod(){
       BaseInterface a = factory.getInstance(some parameters);
       // do whatever with instance a
    }
}

回答by Sudip Bhandari

It has been rightly answered here that in such cases where an interface is implemented by more than one class we have to name each of those beans using @Component(name=$beanName)

这里的答案是正确的,在接口由多个类实现的情况下,我们必须使用命名这些 bean 中的每一个 @Component(name=$beanName)

I would like to add one more point that in such cases Spring can even autowire such beans in a map:

我想再补充一点,在这种情况下,Spring 甚至可以在地图中自动装配此类 bean:

@Autowired
Map<String,InterfaceName> interfaceMap;
//this will generate beans and index them with beanName as key of map

回答by Adriaan Koster

An even cooler solution is to let the implementations themselves contain the logic to determine if they are applicable. You can inject all the available implementations as a collection and iterate over them to find one (or more, if that's what you need) applicable ones:

一个更酷的解决方案是让实现本身包含确定它们是否适用的逻辑。您可以将所有可用的实现作为一个集合注入并迭代它们以找到一个(或多个,如果这是您需要的)适用的:

public interface BaseInterface {
    boolean canHandle(Object parameter);
    Object doTheWork(Object parameter);
}

@Service
public class SomeService {

    private final BaseInterface[] implementations;

    // Spring injects all beans implementing BaseInterface
    public MessageService(BaseInterface... implementations) {
        this.implementations = implementations;
    }

    public Object doSomething(Object parameter) {
        BaseInterface baseInterface = findBaseInterface(parameter);
        return baseInterface.doTheWork(parameter);
    }    

    private BaseInterface findBaseInterface(Object parameter) {
        return Arrays.stream(implementations)
            .findAny(i -> i.canHandle(parameter)
            .orElseThrow(new MyRuntimeException(String.format("Could not find BaseInterface to handle %s", parameter)));
    }
}