We will create examples on Spring @ConditionalOnMissingBean. Anywhere we define a Spring bean, we can optionally add a condition. Only if the specified condition is satisfied then only bean will be added to the application context. To declare a condition, we can use any of the @Conditional… annotations. The @ConditionalOnMissingBean annotation lets a bean be included based on the absence of specific beans. By default Spring will search entire hierarchy (SearchStrategy.ALL).

Prerequisites

Eclipse Neon, Java 1.8, Gradle 5.4.2, Spring Boot 2.1.7

Creating Project

Create a gradle based project in Eclipse with the project name as spring-conditional-on-missing-bean.

Updating Build Script

The default generated build.gradle script does not include required dependencies, so we will update the build script as follows to include the spring boot dependencies.

buildscript {
	ext {
		springBootVersion = '2.1.7.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
	mavenLocal()
    mavenCentral()
}
dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
}

Creating Service Class

We will create simple service class without any methods in it. Ideally this service class will have some methods to perform your project’s business operations.

package com.roytuts.spring.conditional.on.missing.bean;
public class SpringService {
}

Creating Main Class

We will create a main class to run our Spring Boot application. We will not modify our main class at all, that’s why we are writing the source code of main class here.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringConditionalOnMissingBeanApp implements CommandLineRunner {
	@Autowired
	private SpringService springService;
	public static void main(String[] args) {
		SpringApplication.run(SpringConditionalOnMissingBeanApp.class, args);
	}
	@Override
	public void run(String... args) throws Exception {
		System.out.println("Spring service fully qualified class name: " + springService.getClass());
	}
}

Examples on @ConditionalOnMissingBean

We will create various ways of using @ConditionalOnMissingBean annotation.

We will create a Spring Config class and use annotation @ConditionalOnMissingBean in various ways, such as, by nametypevalue, annotation and will see how it works.

The same Spring Config class will be modified to show different examples.

@ConditionalOnMissingBean – by type

You can specify by type by passing type or if you do not pass any value then default value is by type.

In the below example, the springService bean is going to be created if no bean of type SpringService is already contained in the ApplicationContext.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	// @ConditionalOnMissingBean
	@ConditionalOnMissingBean(type = "SpringService")
	public SpringService springService() {
		return new SpringService();
	}
}

When you run the main class you will see below output:

Spring service fully qualified class name: class com.roytuts.spring.conditional.on.missing.bean.SpringService

Now if you put @Service annotation on SpringService class and try to run the main class, you will see error in console because the bean already exists in the ApplicationContext.

The bean 'springService', defined in class path resource [com/roytuts/spring/conditional/on/missing/bean/SpringConfig.class], could not be registered. A bean with that name has already been defined in file

You may also pass multiple types as @ConditionalOnMissingBean(type = { "SpringService", "AnotherSpringService" }).

@ConditionalOnMissingBean – by value

Now we will pass by value.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(value = SpringService.class)
	public SpringService springService() {
		return new SpringService();
	}
}

You will get the same output as we had seen for type.

You can specify multiple classes for value as @ConditionalOnMissingBean(value = { SpringService.class, AnotherSpringService.class }).

@ConditionalOnMissingBean – by name

We will pass bean’s name for the name.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(name = "springService")
	public SpringService springService() {
		return new SpringService();
	}
}

Now running the main class will give the same output.

For multiple names you can use @ConditionalOnMissingBean(name = { "springService", "anotherSpringService" }).

@ConditionalOnMissingBean – by search

You can use by search and for this to work you have to use any of the following SearchStrategy.

ALL - searches the entire hierarchy
ANCESTORS - searches all ancestors, but not the current context
CURRENT - searches only the current context

For our example, we will get result for SearchStrategy.ALL and SearchStrategy.CURRENT because no ancestor found, so we will get null pointer exception if we try to execute using SearchStrategy.ANCESTORS.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
	public SpringService springService() {
		return new SpringService();
	}
}

By running the main class you will get the same output.

@ConditionalOnMissingBean – by annotation

Update the Spring Config class by passing value for the annotation.

Make sure your class does not have the annotation which is passed as a value for annotation.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(annotation = Service.class)
	public SpringService springService() {
		return new SpringService();
	}
}

By running the main class you will see the same output.

You may also pass multiple annotations using @ConditionalOnMissingBean(annotation = { Service.class, Repository.class }).

@ConditionalOnMissingBean – by ignored

You can ignore a class or classes, even the same class you are creating bean for.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(ignored = AnotherSpringService.class)
	public SpringService springService() {
		return new SpringService();
	}
}

To ignore multiple classes, use @ConditionalOnMissingBean(ignored = { SpringService.class, AnotherSpringService.class }).

@ConditionalOnMissingBean – by ignoredType

In the above example we passes class or classes but here we will pass type or types of the class or classes.

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(ignoredType = "AnotherSpringService")
	public SpringService springService() {
		return new SpringService();
	}
}

For multiple ignore types, you can use @ConditionalOnMissingBean(ignoredType = { "SpringService", "AnotherSpringService" }).

@ConditionalOnMissingBean – by parameterizedContainer

Spring documentation says

Additional classes that may contain the specified bean types within their generic parameters. For example, an annotation declaring value=Name.class and parameterizedContainer=NameRegistration.class would detect both Name and NameRegistration<Name>.

Creating Parameterized Class

Create below class which will be used as a parameterized class into another class.

package com.roytuts.spring.conditional.on.missing.bean;
public class RequiredBean {
}

Create parameterized class with the below source code.

package com.roytuts.spring.conditional.on.missing.bean;
public class AnotherSpringService<T extends RequiredBean> {
}

Now we will update Spring Config Class as follows:

package com.roytuts.spring.conditional.on.missing.bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
	@Bean
	@ConditionalOnMissingBean(parameterizedContainer = AnotherSpringService.class)
	public SpringService springService() {
		return new SpringService();
	}
	@Bean
	public AnotherSpringService<RequiredBean> anotherSpringService() {
		return new AnotherSpringService<RequiredBean>();
	}
}

Running the main class will give you the same output.

By same output for all the above cases I meant to say that you will get successfully bean created for SpringService.

That’s all.

Source Code

You can download source code.

Thanks for reading.

Tags:

Leave a Reply

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