在Spring中注入不同bean的多个实例

时间:2018-02-15 14:08:23

标签: java spring dependency-injection

我正在尝试解决Spring DI的问题。我有两个bean(MyFirstBean& MySecondBean),它们都实现了一个给定的接口(MyBean)。然后我有多个其他bean(例如OtherBean),我想与两个bean中的任何一个一起使用。 OtherBean的自动装配显然失败,因为有多个MyBean个实例可供选择。是否有可能一般地创建每个bean的两个实例  自动装配MyBean并使用限定符引用它们?我知道这可以通过编写配置类来实现,但由于所有这些都是API的一部分,我希望尽可能降低开销。

现状:

public interface MyBean {
}

@Component
public class MyFirstBean implements MyBean {
}

@Component
public class MySecondBean implements MyBean {
}

@Component
public class OtherBean {

    final MyBean myBean; // error due to multiple beans

    public OtherBean(MyBean myBean) {
        this.myBean = myBean;
    }
}

期望情况:

@Component
public class SomeBean {

    final OtherBean myBeanUsingFirstBean; // internally autowires MyFirstBean

    final OtherBean myBeanUsingSecondBean; // internally autowires MySecondBean

    public SomeBean(
        @FirstBeanQualifier OtherBean myBeanUsingFirstBean,
        @SecondBeanQualifier OtherBean myBeanUsingSecondBean) {
        this.myBeanUsingFirstBean = myBeanUsingFirstBean;
        this.myBeanUsingSecondBean = myBeanUsingSecondBean;
    }
}

5 个答案:

答案 0 :(得分:5)

解决方案1:

spring autowires bean的方法之一就是名字。如果没有指定,spring将使用类名创建bean(带小的首字母),因此对于MyFirstBean,bean名称将是myFirstBean。知道你可以通过将属性的名称更改为最终的MyBean myFirstBean来自动装配所需的bean

public interface MyBean {
}

@Component
public class MyFirstBean implements MyBean {
}

@Component
public class MySecondBean implements MyBean {
}

@Component
public class OtherBean {

    // this way spring will inject instance of MyFirstBean
    @Autowired
    final MyBean myFirstBean ; 

}

解决方案2:

有时我喜欢手动分配bean。所以我将所有可用的bean自动装入列表中,然后在@PostConstruct中使用逻辑:

@Autowired
private List<MyBean> myBeans;

解决方案3:

使用@Qualifier注释

public interface MyBean {
}

@Component("fooBean")
public class MyFirstBean implements MyBean {
}

@Component
public class MySecondBean implements MyBean {
}

@Component
public class OtherBean {

    @Autowired
    @Qualifier("fooBean")
    final MyBean myFirstBean ; 

}

解决方案4:

自定义注释

@Qualifier
@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, 
ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBeanType {
   String value();
}


public interface MyBean {
}

@MyBeanType("fooBean")
@Component()
public class MyFirstBean implements MyBean {
}

@MyBeanType("barBean")
@Component
public class MySecondBean implements MyBean {
}

@Component
public class OtherBean {

    @Autowired
    @MyBeanType("Foo")
    final MyBean myBean ; 

}

答案 1 :(得分:1)

您可以将@Qualifier添加到bean中以区分不同的bean。注入bean时,您可以使用指定的限定符注入正确的限定符。

答案 2 :(得分:0)

如果代码应该像OP所描述的那样完全,那么自定义注释就足够了:

@Qualifier("myFirstBean")
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstBeanQualifier {}

@Qualifier("mySecondBean")
@Retention(RetentionPolicy.RUNTIME)
public @interface SecondBeanQualifier {}

@Component
public class OtherBean {
    private final MyBean myBean1;
    private final MyBean myBean2;

    public OtherBean(@FirstBeanQualifier MyBean myBean1,
                     @SecondBeanQualifier MyBean myBean2) {
        this.myBean1 = myBean1;
        this.myBean2 = myBean2;
    }
}

答案 3 :(得分:0)

我认为这可能是最简单的事情。

创建一个处理器,请求MyBean类型的对象

@Component
public class ProcessorFactory {

    @Autowired private MyFirstBean myFirstBean;
    @Autowired private MySecondBean mySecondBean;


    public MyBean getProcessor(arg) {
        if (arg == SomeValue1) {
            return myFirstBean;
        }else{
            return mySecondBean;
        }

    }
}

用法类看起来像这样

@Service
public class SomeServiceClass{
    @Autowired private ProcessorFactory processorFactory;
    //Other dependencies

    void doSomething(Some args){

        MyBean = processorFactory.getProcessor(arg);
       //Do something with the object

    }

}

答案 4 :(得分:0)

尽管问问者希望避免编写Configuration类,但我使用一个实现了一个解决方案,并且想证明它确实还不错。

这是我的配置类:

@Configuration
public class ApplicationContextOtherBeanQualifier {

    @Autowired
    @Qualifier("myFirstBean")
    private MyBean myFirstBean;

    @Autowired
    @Qualifier("mySecondBean")
    private MyBean mySecondBean;

    // Here is how you get two different instances of OtherBean
    // while using the same implementation:

    @Bean
    public OtherBean otherBeanUsingFirstBean() {
        return new OtherBean(myFirstBean);
    }

    @Bean
    public OtherBean otherBeanUsingSecondBean() {
        return new OtherBean(mySecondBean);
    }
}

现在,您可以使用@Resource@Qualifier批注将其适应所需的情况:

@Component
public class SomeBean {

    @Resource
    @Qualifier("otherBeanUsingFirstBean")
    private OtherBean otherBeanUsingFirstBean; // internally autowires MyFirstBean

    @Resource
    @Qualifier("otherBeanUsingSecondBean")
    private OtherBean otherBeanUsingSecondBean; // internally autowires MySecondBean

}

请尝试一下,让我知道它是否适合您!