Description
Consider the case when there are two (or more) Spring configuration classes, each returning a bean with the same name (e.g. baseBean
). Also, suppose that these config classes create other beans (e.g. anotherBean1
and anotherBean2
), which contain baseBean
instances.
Is there a way to make Spring inject the baseBean
instances created in the same configuration class?
Example
// Beans
public abstract class BaseBean {
public abstract String getName();
}
public class Bean1 extends BaseBean {
@Override
public String getName() {
return "Bean1";
}
}
public class Bean2 extends BaseBean {
@Override
public String getName() {
return "Bean2";
}
}
// Configurations
@Configuration
public class Config1 {
@Bean
public BaseBean baseBean() {
return new Bean1();
}
@Bean
public AnotherBean anotherBean1() {
return new AnotherBean(baseBean());
}
}
@Configuration
public class Config2 {
@Bean
public BaseBean baseBean() {
return new Bean2();
}
@Bean
public AnotherBean anotherBean2() {
return new AnotherBean(baseBean());
}
}
// Main class
public class Main {
public static void main(String [] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config1.class, Config2.class);
AnotherBean anotherBean1 = (AnotherBean) applicationContext.getBean("anotherBean1");
AnotherBean anotherBean2 = (AnotherBean) applicationContext.getBean("anotherBean2");
System.out.println(anotherBean1.baseBean.getName());
System.out.println(anotherBean2.baseBean.getName());
}
}
Expected output:
Bean1
Bean2
Actual output:
Bean2
Bean2
In other words, both Config1.anotherBean1()
and Config2.anotherBean2()
invoke Config2.baseBean()
. Instead, I would like each Config
class to invoke its own baseBean
method, i.e. Config1.anotherBean1()
should use Config1.baseBean()
(and Config2.anotherBean2()
should of course use Config2.baseBean()
).
Is there a built-in way to force this behaviour somehow, e.g with some annotation?
Remarks
- By "built-in" I mean, that I would not like to write
BeanPostProcessor
's, or other similar extension. I would like to add an already existing annotation, or some similar solution. - Adding
@Primary
,@Qualifier
, etc. is not an option: while it would work in the above example, the concrete code is structured in such a way, that does not make this possible. More precisely,baseBean()
is an abstract method in an abstract base class, which is invoked by another method,anotherBean()
in the same abstract class. Concrete classes then provide implementation forbaseBean
method. This means, that in the base class I obviously cannot use any qualifier to refer tobaseBean
(and cannot make any of the implementations@Primary
either).
public class BaseConfig {
@Bean
public abstract BaseBean baseBean();
@Bean
public AnotherBean anotherBean() {
return new AnotherBean(baseBean());
}
}
+----------------------------+
| BaseConfig |
| |
| + (abstract) baseBean() |
| + anotherBean() |
|____________________________|
/\
/__\
_________|__________
_______|_______ _______|__________
| Config1 | | Config2 |
| + baseBean() | | + baseBean() |
|_______________| |________________|
- All beans need to be singleton, i.e. not prototype.
- I strongly suspect that this is not possible (I checked what Spring does under the hood, and it seems that it maintains a "bean name" -> bean instance map, which is filled with whichever bean is encountered first. And then, the call to the Java config method is intercepted, and the already cached bean is returned) So a confirmation, that what I want is indeed not possible, is also a valid answer. But I could also be wrong on this...
System.out.println(applicationContext.getBeansOfType(BaseBean.class).size());
will print "1"