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

43,470

Solution 1

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

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

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

then at @autowired annotation

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

Solution 2

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

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

Solution 3

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.

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

Solution 4

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)));
    }
}
Share:
43,470

Related videos on Youtube

0x822a5b87
Author by

0x822a5b87

Updated on April 04, 2020

Comments

  • 0x822a5b87
    0x822a5b87 about 4 years

    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:

    @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 BaseInterface type.

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

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

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

    can anyone help me ?

    • gdbj
      gdbj over 4 years
      It's times like this that I hate Java
    • MartinL
      MartinL about 4 years
      @gdbj facepalm it's about spring, not java You would have the same problem in any other language, that has DI. If you try to wire 2 beans to one field, you will have this
  • 0x822a5b87
    0x822a5b87 almost 8 years
    Thanks for your answer, but in the sentence @Qualifier("the name of the desired bean") , can "the name of the desired bean" can be a dynamic string? beacause the AutowiredClazz maybe use the different Implementation in different situation.
  • Mohamed Nabli
    Mohamed Nabli almost 8 years
    spring cannot handle this, because the AutowiredClazz will be initialized just once, but i noticed that you have a getInstance() method wich will return the apropriate bean. this will make the usage dynamic
  • gdbj
    gdbj over 4 years
    You would think that Spring would implement a simple method of trying to pick the right one, rather than presenting a run-time only error here that will be very hard to solve before presenting the user with an error. Pretty terrible implementation, IMO.