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

Java 在Spring中注入不同bean的多个实例,java,spring,dependency-injection,Java,Spring,Dependency Injection,我正试图解决弹簧DI的一个问题。我有两个bean(MyFirstBean&MySecondBean),它们都实现了给定的接口(MyBean)。然后我有多个其他bean(例如,OtherBean),我想与这两个bean中的任何一个一起使用。对于OtherBean,自动连线显然失败,因为有多个MyBean实例可供选择。是否有可能为每个bean创建两个实例 自动连接MyBean并使用限定符引用它们?我知道通过编写一个配置类可以做到这一点,但由于所有这些都是API的一部分,因此我希望尽可能降低开销 当前

我正试图解决弹簧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;
    }
}
所需情况:

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

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

解决方案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;
解决方案4: 自定义注释

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 ; 

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

}
如果代码看起来与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;
    }
}

我认为这是一个人能做的最简单的事情

创建一个处理器,请求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;
        }

    }
}
usage类看起来像这样

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

    void doSomething(Some args){

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

    }

}

尽管询问者希望避免编写配置类,但我使用配置类实现了一个解决方案,并且我想表明它真的没有那么糟糕

这是我的配置类:

@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

}

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

用正确的@Qualifier注释bean类,然后用所需的限定符自动连接bean。细微的差别很难解释。我可以向
OtherBean
添加一个限定符,从而解决冲突。但是我需要两个bean
OtherFirstBean
,它使用
MyFirstBean
,并进行相应的注释,还有
OtherSecondBean
,它使用
MySecondBean
。这不是我想要的。我想在自动连接
OtherBean
时使用一个注释,并决定内部使用哪个bean
OtherBean
。那么处理器(实现工厂模式)如何,它将在每次您要求它返回正确的实例时返回给您?工厂能否对不同的限定符注释作出反应?一个解决方案到底是什么样的?@fynn Checkout下面我的答案这是一个不错的解决方案,但我的问题是它需要一个额外的参数传递给doSomething方法,以便调用getProcessor来检索MyBean所需的实现。我希望不必更改SomeServiceClass的方法签名。有没有一种方法可以创建SomeServiceClass的两个不同实例,一个是MyFirstBean被注入的实例,另一个是MySecondBean被注入的实例?@JohnPeterThompsonGarcés看看重写
getProcessor
是否能帮到你。[警告]这是一种糟糕的方法