这是我的问题:我有一个基本接口和两个实现类。
Service类依赖于基接口,代码如下:
@Component
public interface BaseInterface {}
@Component
public class ClazzImplA implements BaseInterface{}
@Component
public class ClazzImplB implements BaseInterface{}
配置如下:
@Configuration
public class SpringConfig {
@Bean
public BaseInterface clazzImplA(){
return new ClazzImplA();
}
@Bean
public BaseInterface clazzImplB(){
return new ClazzImplB();
}
}
服务类依赖于基接口将决定通过某些业务逻辑自动装配哪个实现。代码是这样的:
@Service
@SpringApplicationConfiguration(SpringConfig.class)
public class AutowiredClazz {
@Autowired
private BaseInterface baseInterface;
private AutowiredClazz(BaseInterface baseInterface){
this.baseInterface = baseInterface;
}
}
IDEA抛出异常:无法自动装配。有多个BaseInterface
类型的bean。
虽然可以使用@Qualifier解决,但在这种情况下我无法选择依赖类。
@Autowired
@Qualifier("clazzImplA")
private BaseInterface baseInterface;
我尝试阅读spring文档并提供 Constructor-based dependency injection
,但我仍然对这个问题感到困惑。
任何人都可以帮助我吗?
答案 0 :(得分:13)
Spring在您在配置类中声明的两个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();
}
}
然后在@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;
}
}
答案 1 :(得分:3)
仅使用spring框架无法解决这个问题。您提到基于某些逻辑,您需要一个BaseInterface实例。可以使用Factory Pattern解决此用例。创建一个实际上是BaseInterface工厂的Bean
@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
public class SpringConfig {
@Bean(name="clazzImplA")
public BaseInterface clazzImplA(){
return new ClazzImplA();
}
@Bean(name="clazzImplB")
public BaseInterface clazzImplB(){
return new ClazzImplB();
}
}
服务类
@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
}
}
答案 2 :(得分:1)
如果使用@Autowired
,Spring会搜索与您要自动装配的字段类型匹配的bean。在您的情况下,有多个BaseInterface
类型的bean。这意味着Spring无法明确地选择匹配的bean。
在这种情况下,你没有其他选择明确说明Spring应该使用的bean或解决歧义。
答案 3 :(得分:1)
一个更酷的解决方案是让实现本身包含确定它们是否适用的逻辑。您可以将所有可用的实现作为一个集合注入,并遍历它们以找到一个(或多个,如果需要的话)适用的实现:
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)));
}
}
答案 4 :(得分:0)
这里已经正确回答了,在这样的情况下,如果一个接口由多个类实现,我们必须使用@Component(name=$beanName)
来为每个bean命名。
我想补充一点,在这种情况下,Spring甚至可以在地图中自动装配此类bean:
@Autowired
Map<String,InterfaceName> interfaceMap;
//this will generate beans and index them with beanName as key of map