Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting multiple instances of different beans in Spring

I am trying to solve a problem with the Spring DI. I have two beans (MyFirstBean & MySecondBean) that both implement a given interface (MyBean). Then I have multiple other beans (e.g. OtherBean) that I want to use with either one of the two beans. Autowiring obviously fails for OtherBean since there are multiple instances of MyBean to choose from. Is there any possibility to generically create two instances of each bean that autowires MyBean and refer to them using qualifiers? I know this is possible by writing a configuration class but since all this is part of an API, I want to keep the overhead as low as possible.

Current Situation:

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

Desired Situation:

@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;
    }
}
like image 386
Fynn Avatar asked Feb 15 '18 14:02

Fynn


People also ask

How do you inject multiple beans of the same type?

The default autowiring is by type, not by name, so when there is more than one bean of the same type, you have to use the @Qualifier annotation.

Can we create multiple beans of same class in Spring?

Yes, you can do it with a help of your custom BeanFactoryPostProcessor implementation. Here is a simple example. Suppose we have two components. One is dependency for another.

Can we configure two beans of the same class with the different ID in Spring?

If you define two beans of same class, without different bean id or qualifiers ( identifier) , Spring container will not be able to understand which bean to be loaded , if you try to access the bean by passing the classname and you will get NoUniqueBeanDefinitionException as there are two qualifying TestBean.


Video Answer


2 Answers

Solution 1 :

One of the ways spring autowires beans is by name. If not specified spring will create bean using class name (with small first letter) so for MyFirstBean , bean name will be myFirstBean. Knowing that you can autowire desired bean by changing the name of the property to final MyBean myFirstBean

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 ; 

}

Solution 2 :

Sometimes i like to manually assign beans. So i autowire all available beans into list like so, and then later in @PostConstruct u do the logic :

@Autowired
private List<MyBean> myBeans;

Solution 3 :

Using @Qualifier annotation

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 ; 

}

Solution 4:

Custom annotation

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

}
like image 175
SeaBiscuit Avatar answered Oct 03 '22 02:10

SeaBiscuit


Although the asker wants to avoid writing a Configuration class, I implemented a solution using one and I want to show that it's really not so bad.

Here is my Configuration class:

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

Now, you can fit this into your Desired Situation using the @Resource and @Qualifier annotations:

@Component
public class SomeBean {

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

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

}

Please try this out and let me know if it works for you!

like image 37
John Thompson Avatar answered Oct 03 '22 00:10

John Thompson