Spring Boot In Action: Bean overriding under the hood

Tram Ho

I. Introduction

We know that Spring Boot does not allow two beans with the same name because suppose if there are two beans named myBean how can Spring Boot know which bean to return when the following code is executed?

At this point Spring Boot will have to choose between the two and thus may lead to unexpected results. Therefore, from Spring Boot version 2.1 onwards, Spring Boot only allows each bean to have a unique name.

Of course, this is only on a theoretical level. Let’s look at the example below to understand better.

I have an entity AppBean and two Configuration classes that respectively declare its 2 beans with the same name as appBean as follows:

AppBean.java

MyConfig1.java

MyConfig2.java

Now I will write a Main function to get the appBean bean to use. Can you guess what will happen with the code below?

Main.java

Are you expecting an error message like this?

A bean with that name has already been defined in class path ….

Unfortunately, what you may have been waiting for (including me) did not happen. The program still runs fine and silently prints the following in the console:

from config 2

Try to guess why not from config 1 but from config 2 , I will explain in detail in the next section.

II. Analyze Spring Boot’s source code

In the code in part I , because I declare the bean in @Configuration class, to get the bean to use it, I need to go through AnnotationConfigApplicationContext :

ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

Initializing the AnnotationConfigApplicationContext instance should do the following things:

image.png

1. this()

Initialize reader and scanner

2. register(componentClasses);

Register componentClass will register bean with DefaultListableBeanFactory.

image.png

After we have registered the componentClass ( @SpringBootApplication ) Main , we already have a bean named main with the corresponding class.

image.png

Since the class marked as @SpringBootApplication will scan the current package and sub-packages to find the bean, you must have figured out what Spring Boot will do next =)).

When @Configuration classes are provided as input, the @Configuration class itself is registered as a bean definition, and all declared @Bean methods within the class are also registered as bean definitions.

3. refresh();

In this function Spring Boot handles a lot of different things, but in the scope of this article what we need to care about most is the invokeBeanFactoryPostProcessors method that will be used to load and initialize the bean.

image.png

To reduce rambling I will always go to the ConfigurationClassPostProcessor class to see how the flow goes. Callstack I will put at the end of the article.

image.png

The idea is basically to find the base packages of the component class and then scan @Configuration class thui.

image.png

image.png

Callstack:

After the scan is complete, we can add the following 2 configClasses:

image.png

Now comes the important stage of loading/registering BeanDefinitions from configClasses.

image.png

We can see that when registering the bean, the allowBeanDefinitionOverriding variable of the DefaultListableBeanFactory class instance has the value = true. So in case the appBean bean already exists, no Exception is thrown. And appBean bean coming from the configuration class MyConfig2 will override the bean of the same name that comes from the class MyConfig1 . This is completely understandable because Spring bean default is singleton scope.

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

image.png

image.png

image.png

Callstack:

III. Run with SpringApplication.run(Main.class, args)

From Spring boot 2.1 , bean overriding disabled by default, so if you execute the following code, from version 2.1 onward, you will get an error.

image.png

image.png

Although disabled by default, we can still override beans by changing the settings in application.yml

IV. References

===

Thanks for reading.

Share the news now

Source : Viblo