#SpringBoot : Create a conditional Spring Boot Bean only if asked by an external configuration, not using @Conditional #Java

By | September 7, 2022

Usually Spring Boot beans are created automatically before the application start and autowired as needed.

What if we need to create a Spring bean only if some configuration file requires it.

This way of writing code is very useful when trying to write highly configurable enterprise applications where you want to be able what components are instantiated at runtime.

When you need to create a Spring Bean only if some conditions are met Spring Boot provides the @Conditional annotation.

Indicates that a component is only eligible for registration when all specified conditions match.

A condition is any state that can be determined programmatically before the bean definition is due to be registered (see Condition for details).

The @Conditional annotation may be used in any of the following ways:

  • as a type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes
  • as a meta-annotation, for the purpose of composing custom stereotype annotations
  • as a method-level annotation on any @Bean method

If a @Configuration class is marked with @Conditional, all of the @Bean methods, @Import annotations, and @ComponentScan annotations associated with that class will be subject to the conditions.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html

The issue with this annotation is that is a static annotation, cannot be used at runtime to be linked to some runtime condition.

Yes you can add in the code that a bean is created only if you run on a test environment or in debug mode or any other specific well defined static set-up for your system.

When for example we need to instantiate a Spring Bean only if some entry is specified in an configuration file loaded at runtime by the application we cannot use this simple annotation.

The way to do it is to use a bean factory.


/**
 * AdapterInput factory
 * 
 * @author gvoina
 *
 */
@Configuration
public class AdapterInputBeanConfiguration {
	@Bean
	public AdapterInput adapterInput(AdapterConfiguration config, MessageBeanRepository messageBeanRepository,
	                                                                         PendingRenameRepository pendingRenameRepository,
	                                                                         AdapterInputHelper adapterInputHelper) {
		Map<String, AdapterInputConfiguration> inputConfigurations = config.getInputConfigurations();
		for (Entry<String, AdapterInputConfiguration> entry : inputConfigurations.entrySet()) {
			if (AdapterInput.class.isAssignableFrom(entry.getValue().managedInput)) {
				return new AdapterInput(entry.getValue(), messageBeanRepository, pendingRenameRepository, interfaceAdapterInputHelper);
			}
		}
		return null;
	}
}

As you can see the AdapterInput bean is created only if conditions are met, else a null value is returned.

Very important resulting bean must be injected using @Autowired(required = false). This will ensure that even if the conditions are not met and bean factory is not creating the bean the main application is able to set the returned null in Autowire.

Bellow is an example of the setter for this bean in the main application.

@SpringBootApplication
@EnableJpaRepositories(repositoryBaseClass = CustomRepositoryImpl.class)
public class AdapterSpringBootApplication {
...
protected AdapterInput adapterInput;
...

        @Autowired(required = false)
	public void setAdapterInput(AdapterInput adapterInput) {
		this.adapterInput = adapterInput;
	}
...
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.