Java Beans are eagerly created in Spring Boot applications by default. That includes dependency beans. This is something I need to remember always. I tend to forget it and take it for granted since Spring Boot is great at spoiling Java developers. While that is a boon in most times, it can easily become a bane on other instances.
What happened was that there was this sort of proof-of-concept functionality an application had that should be disabled in production environment. It was just something that produced data to a streaming server. I say PoC because it wasn’t in the original design, but it was added to see if it works. Besides, it didn’t have the necessary permissions to be able to connect to a live production server.
Rather than remove this feature, it was deemed better to create an on/off switch for it. The default would be at a “turned off” state – don’t produce data. The switch was made, a simple property that had a true/false value that the app can pick up at startup. It was working as it should.
Silly me, as always, overlooked the little details and did not factor in the components that were being used by this PoC feature. While the toggle was there to stop the method call to produce data, it was just that, at the method level. The related components were still “hot”. Of course, when the application started and could not find the correct properties to make the connections, it crashed! *facepalm*
To avoid loading the Beans at start when the functionality is not gonna be used anyway, we can use 2 annotations. Perhaps there are more, but these are the ones I experimented on.
First is @Lazy or lazy-loading. Then there is the @ConditionalOnProperty where Spring will look for that property to determine whether to create the beans or not.
Both solutions are viable. It all depends on the use case.
@Lazy can be placed at the class or method level. Say we have that @Configuration class. That class can be tagged entirely to be lazy-loaded. All the tagged Beans in there will follow. They won’t get initialized eagerly. Any other Spring controlled components that are referencing said Beans will have to be lazy-annotated as well. Like those @Autowired fields in the application. The annotation can be selectively placed on @Bean definitions. This can be also used in @Component definitions too.
For the second one, it is that the Bean is created on condition based on a property. By default if the property is absent, the condition would be false – Bean is not created. If the property is found, the condition becomes true. The @ConditionalOnProperty can be tricky. If there was no value explicitly given to check on an existing property, the Bean will still get initialized. Placing that extra
havingValue=something, will lead to further evaluation on whether property does have the value of something to get the bean created. Having a non-present property default to a false condition can be overriden using
Lastly, having a Bean autowired in a service class, for example, when referenced bean has @ConditionalOnProperty, can be set to @Autowired(required=false). Note that @Autowired can be use in method parameters as well. This is set true by default, so unless it should not be required (false) we don’t have to explicitly do that. This prevents errors where that referencing class will look for that Bean, but it’s not initialized in the context resulting in a failure.
In the end I went for the second annotation, @ConditionalOnProperty. There was already that on/off property being checked. It was more logical to make use of it as it was intended.
- > Nested Java Bean Validation May 27, 2021
- > Chaining Profiles and Additional Documents February 24, 2022
- > Access EntityManager From Spring Data JPA In Spring Boot October 20, 2020
- > Spring OAuth2 Using Password Grant Type With Additional Headers February 24, 2021
- > Deserializing JSON Object With Unknown Properties October 15, 2020