Reputation: 99
in our software we are using spring java config. We have a setup, where one configuration extends an abstract configuration. Please have a look at this testcase:
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class SpringConfigTest {
@Test
public void test() {
final AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
ctx.getBeansOfType(AtomicInteger.class).entrySet().stream().forEach(b -> System.out.println(b.getKey() + " : " + b.getValue() + " (" + b.getValue().hashCode() + ")"));
}
@Configuration
public static class MyConfig extends AbstractConfig {
@Bean(name = "anotherName")
public AtomicInteger myBean() {
return new AtomicInteger(5);
}
}
public static abstract class AbstractConfig {
@Bean
public AtomicInteger myBean() {
return new AtomicInteger(10);
}
}
}
The idea is, that MyConfig
overwrites AbstractConfig
and in the created ApplicationContext there is only one bean of type AtomicInteger
under the name anotherName
.
The results was:
anotherName : 5 (2109798150) myBean : 5 (1074389766)
So it says, there are two beans (two instances - one for each name) - and even more surpring: the same method (MyConfig#myBean()
) was used to create both of them.
This behaviour looks odd to us: We expected that spring would either respect the usual java way of inheritance and create only the bean from MyConfig
... or at least would create two independent beans ("10" and "5") in case it sees AbstractConfig
as an independant config.
While investigating this we also tried to register the method name on the MyConfig
class:
public static class MyConfig extends AbstractConfig {
@Bean(name = ["anotherName", "myBean"])
public AtomicInteger myBean() {
...
and this time we got only one bean:
anotherName : 5 (2109798150)
.. what was even more surprising for us.
Does anybody know if this is really the correct behaviour or are we only using it wrong? Should we raise a ticket in spring's jira? Thanks in advance!
Upvotes: 2
Views: 5057
Reputation: 71
In Spring, beans can be wired by type first, but then by name. Hence @Qualifier("myBeanName") can disambiguate autowiring multiple beans with the same type, e.g.
So: the very fact that the non-abstract bean has been given another name, causes it to be considered a different bean in the application context.
You can declare beans in a non-configuration class. That is called "lite" mode, but it is still a bean in the application context. See also this answer on lite mode.
I didn't know one could give a bean more than one name, but since Spring beans are singletons by default, it stands to reason only one bean would be created in the second case, as "myBean" already exists and only one bean with that name can be in the application context.
Upvotes: 0
Reputation: 3485
I'm not a Spring pro, but I'd say that behaviour is by design. To achieve what you want (I hope I guessed right) "inject this bean instead of the other" you would use @Primary on a bean, to selectively enable a configuration depending on circumstances you would use a @Conditional i.e. @Profile.
Upvotes: 1