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;
}
}
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.
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.
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.
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 ;
}
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;
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 ;
}
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 ;
}
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With