Spring无法自动装配,有多个``type

时间:2016-06-01 09:49:23

标签: java spring

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

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 ,但我仍然对这个问题感到困惑。

任何人都可以帮助我吗?

5 个答案:

答案 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