tvvk
tvvk

Reputation: 99

Spring java config extending an abstract configuration

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

Answers (2)

Wardibald
Wardibald

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

Daniel Hiller
Daniel Hiller

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

Related Questions