When two beans collide

„When Two Worlds collide
So who will survive
There’s no place to hide
When two worlds collide”

― Iron Maiden, “When Two Worlds Collide”. Virtual XI.

Let’s face it – there are features of Spring that many of us probably do not know about. With such amount of code and numerous pages of documentation as Spring framework has, it is virtually impossible to know them all.

Today I would like to tell you about one mechanism of Spring IoC container that I think is a bit less known amongst programmers. We were not aware of it and it had caused us a bit of confusion lately. It seemed at first as if one of our beans had somehow disappeared.

Case study

In our case we were responsible for implementing integration with a new client, which provided REST API for its services. The first thing we did was writing a client to introduce a layer of abstraction over the external API. Because the message format was JSON, the configuration class for the REST client module contained declaration of a customized ObjectMapper bean from Jackson library. Let’s say this first module was called external-api-client.

The second module we implemented was process-executor, which was responsible for executing processes for this particular client. What is crucial is that its configuration contained second bean of type ObjectMapper. This second mapper had to have a bit different configuration and was supposed to serialize data which we save in our database. We store it for audit purposes and also because sometimes we have to send the same request again.

Of course the external-api-client module configuration was supposed to be imported into the configuration class of process-executor. Knowing that we will have two beans of the same type (two ObjectMappers) we had annotated them with @Qualifier annotation to inject proper instances into desired services. As the result, our whole configuration consisted of classes similar to the ones presented below:

Our App class was quite straightforward and looked more or less like this one:

To our surprise running the application configured this way ended up in a crash with log output resembling following one:

It looked as if Spring could not find the right ObjectMapper to inject into our ProcessExecutor! We were confused at first. The declaration of the missing bean was just below the declaration of ProcessExecutor bean. We checked if we did not make a typo in qualifier name, but all seemed fine. So what had actually happened?

Explanation — bean overriding mechanism

After some time we realized that there was a problem with our Spring context configuration. We missed the fact that our two ObjectMapper beans were constructed by methods with the same name, hence the beans themselves also had conflicting names. It did not struck us at first because we assumed that Spring would throw an exception with a meaningful message informing about the bean name collision. Well, we were wrong.

We fixed the name collision and everything worked like expected. Nevertheless, we were still eager to find out what actually had happened under the hood. I did some debugging and digging over the Internet and finally I got to know!

What was going on in our situation could have been summarized as follows:

  1. Spring created ObjectMapper bean from ProcessExecutorConfig (the one with @Qualifier("requestSerializer")).
  2. Later on Spring encountered ObjectMapper bean declaration from ExternalApiClientConfig. Because the names of the beans were the same the previously instantiated bean ( @Qualifier("requestSerializer")) was overridden by the new one ( @Qualifier("externalApiObjectMapper")).
  3. As the effect, from now on the context contained only one bean of type ObjectMapper qualified as “externalApiObjectMapper”.
  4. When it came time to inject ObjectMapper into ProcessExecutor no bean with appropriate qualifier was found.

So the mechanism which caused us so much confusion is called bean overriding. It is used when Spring encounters a declaration of a bean with the same name as another bean already existing in the context. And it actually informs about it in the logs. We just did not scan the log output carefully enough, cause the detailed information about this whole overriding was there:

At that point I assumed that there is some strict rule regarding bean overriding. I imagined that a bean with the same name from imported configuration is always overridden by the one declared in the configuration class it is imported into (like it happened in our case). Well, I was wrong there too.

The only rule is this: bean with the same name as another one, which is processed later, overrides the older one.

Sanity goes down

Actually it took me some time to come up with the above rule. When I was preparing to write this article I implemented a couple of examples, which behaved in predictable manner (the order of overriding was the same). But then I threw all my examples away and generated a new one, which I thought was better describing our case. To my surprise this last one behaved completely different. If for these previous examples the imported beans were loosing the overriding battle, then for this last one it was the other way around.

I spent almost two hours to debug the last example to find out what was different and why this time things were different. And it turned out that the only difference was the order in which the configuration classes were loaded by a classloader!

For instance, for the configuration classes presented at the beginning of this article, if I changed the name from ExternalApiClientConfig to AExternalApiClientConfig the log output would be different:

Of course it does not depend only on the name of the class. It can vary also with the package or module in which two classes reside. In other words: the amount of factors which can affect the order of processing bean declarations makes it almost impossible to predict which bean will override another. This is especially true for more complex context configurations with multiple imports.

The only thing I am pretty sure of is that without component scanning this order becomes deterministic — with imported configuration classes being processed first. So for instance getting rid of @SpringBootApplication annotation from the App class will somewhat stabilize the situation.

Other situations in which it might hurt you

All of the above observations are just the effect of my experiments with the configuration classes. Frankly speaking I was not able to find anything about bean overriding in the official Spring documentation (if it is somewhere there, please point me to the place). So in the end this whole situation is a bit confusing.

Further thinking about it I realized there are at least two other situations, than the one which we encountered, in which bean overriding might cause some problems:

  1. When you have two beans with the same name in different configuration classes but without qualifiers — in such a case the application will just run without exceptions, but you will end up with a wrong bean in at least one place. In our case for instance if we did not have qualifiers, we would have end up with wrong ObjectMapper serializing our requests into database. Without thorough test such situation could stay unnoticed for a long time and cause serious issues.
  2. When you have two beans with the same name but of different types. At least in such a situation the result will be the crash of the application during start time. That is a case similar to our one with qualifiers.

With the size of the application the complexity will increase. There will be potentially multiple configuration classes spanned across different modules or even projects. So it could be really hard to investigate specific case.

Solution

The obvious solution to prevent such overriding problems is to name beans as specific as possible. Instead of naming both factory methods in our app  objectMapper we should have used more descriptive names as for instance:  externalApiObjectMapper and requestSerializer.

But even if you are careful, there is still a risk that you can choose the same bean name as a one imported from a library or chosen by another developer who implemented different module. And the most danger case here is when the app starts without any error and then behaves in a different way than expected. There is still a log message telling that the overriding took place, but it is easy to miss. Can we do anything about it?

Turning off bean overriding

Fortunately bean overriding is hidden behind a flag of  DefaultListableBeanFactory which can be set by a user. If we do want to turn it off all we need to do is implement our custom ApplicationContextInitializer, like in the example below:

From now on, every time Spring will encounter bean with the same name as the one already existing in the context, exception will be thrown and application will crash during the start time:

It might be a good idea to turn it off by default. If we do so then we will get to know about every bean name conflict right away.

Do we really need so many beans?

Using Spring tempts to make beans from almost any class. Up until now all of the considerations assumed that we still want to register two conflicting beans in the context. But in fact many of such overriding problems arise from the mistake of making beans from objects, which should not necessarily be managed by Spring.

Let’s take our case for example — we had two ObjectMappers, but each of them was injected only into one class where it was supposed to be used. We did not actually need to make beans from them. Actually it was just polluting the context of Spring for which we paid with our time and a lot of confusion. After getting to know what happened we ended up with such a solution:

Instead of making bean from ObjectMapper (only to inject it into one method above) we implemented simple factory method for it. Now, the only interested class — ProcessExecutorConfig — knows about it and ProcessExecutor will be built correctly without any complaints from Spring side.

What I am trying to say is that from time to time it is good to take a step back and reconsider if all our bean declarations are really valid ones. If we inject some of the beans into only one place, then it may be beneficial to not pollute Spring context with them.

Summary

In this article I have described one of the less known mechanisms of Spring IoC container, which is bean overriding. In a nutshell, it happens when Spring encounters bean with the same name as one of the beans already registered in the context. In such a situation the older bean is overriden by the newer one.

As our case study shows, this mechanism can cause sometimes a bit of confusion — especially when the application does have a complex context configuration. Besides the case presented in this article there are also other situations, in which you may observe unintended behavior due to bean overriding. And the fact that the overriding order depends on too many variables can make it really hard to tell what is really going on under the hood.

To prevent such situations you can consider making your bean names as specific as possible. It is also worth to think if all your objects need to be registered in the Spring Context at all. In many cases the good default is to disable the bean overriding mechanism at all — then Spring will throw more meaningful exceptions whenever a collision happens.

I hope that you will find this article interesting and helpful. Who knows, maybe it will save some of your time in the future. In any case I wish you that your Spring context will be always clean!

Please follow and like us:

3 response to "When two beans collide"

  1. By: ArekCzarny Posted: March 12, 2018

    Great read Mateusz. I am using Spring for a while and was not concerned with bean overriding that much, now I will definitely reconsider!! BTW: Really good job on the content of the blog, I think I am reading you guys from the first article of yours it is always high quality and super useful stuff. Keep the good work folks and all the best from Bielsko-Biała!!!!

    • Mateusz Fedkowicz

      By: Mateusz Fedkowicz Posted: March 13, 2018

      Thanks a lot Arek! We are doing our best to deliver the best content we can. Some more interesting articles are coming so keep up with our blog!

  2. By: Bart Swennenhuis Posted: November 10, 2018

    Thanks for figuring this out. While I’m still struggling, this article helps me in finding a solution for my problem. It confirms that spring boot can be unpredictable which worries me.

    Spring makes things worse in my opinion by allowing one to specify @Primary next to an @Bean in a @Configuration. This suggest that it will pick the primary bean if two beans collide, which it does not according to this line from my log:

    This reads like a contradictio in terminis if you ask me.

Leave a Reply

Your email address will not be published. Required fields are marked *