Spring Couldn't autowired,there is more than one bean of `` type
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)));
}
}
Related videos on Youtube
0x822a5b87
Updated on April 04, 2020Comments
-
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 over 4 yearsIt's times like this that I hate Java
-
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 almost 8 yearsThanks 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 almost 8 yearsspring cannot handle this, because the
AutowiredClazz
will be initialized just once, but i noticed that you have agetInstance()
method wich will return the apropriate bean. this will make the usage dynamic -
gdbj over 4 yearsYou 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.