Reputation: 4893
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;
}
}
Upvotes: 10
Views: 18155
Reputation: 1684
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!
Upvotes: 2
Reputation: 2611
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 ;
}
Upvotes: 15
Reputation: 1402
I think this can be the simplest thing one could do.
Create a processor requesting for an object of type 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;
}
}
}
The usage class would look something like this
@Service
public class SomeServiceClass{
@Autowired private ProcessorFactory processorFactory;
//Other dependencies
void doSomething(Some args){
MyBean = processorFactory.getProcessor(arg);
//Do something with the object
}
}
Upvotes: 0
Reputation: 2599
If the code should look exactly like the OP has described, custom annotations will suffice:
@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;
}
}
Upvotes: 0
Reputation: 940
You can add @Qualifier to your beans to distinguish between the different beans. When injecting the beans you can use the specified qualifier to inject the right one.
Upvotes: 1